diff options
112 files changed, 29797 insertions, 0 deletions
@@ -0,0 +1,236 @@ +This file is part of GNU Parted +Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation Inc. + +This file may be modified and/or distributed without restriction. This is +not an invitation to misrepresent who contributed to GNU Parted. + +------------------------------------------------------------------------------- + +We need to keep track of copyright (see the Maintainer HOWTO on www.gnu.org). + + +Leslie Patrick Polzer <polzer@gnu.org> + * parts of 1.6.24 PedUnit API + * GPT pth_* functions and block size fixes + * PED_SECTOR_SIZE -> PED_SECTOR_SIZE_DEFAULT + * debugging framework + * ext2 "strange layout" fix (experimental) + * support for physical block sizes + * SCO BFS support + + http://nic-nac-project.de/~skypher/ + + Snail Mail: + Am Kirchberg 15 + 89537 Giengen + Germany + +Andrew Clausen <clausen@gnu.org> + * all FAT code (libparted/fs_fat) + * all linux-swap code (libparted/fs_linux_swap) + * some reiserfs glue code (libparted/fs_reiserfs) with Umanets + * most of the API (with lots of discussion with Lennert Buytenhek) + (include/parted/*) + * generic filesystem code (filesys.c) and device code (device.c) + * exception code (exception.c, debug.c) + * partition table code (disk.c) + * dos partition support (disk_dos.c) + * mac partition support (disk_mac.c) + * mips partition support (disk_mips.c) + * loopback support (disk_loop.c) + * some of the PC98 code (disk_pc98.c), with Masahiro Sakai. + * misc. hacking on all disk_*.c code + * low-level I/O code (device.c, geom.c, linux.c) + - init_scsi() stolen from gnome-gfdisk via Matt Wilson. + * constraint solver (natmath.c, constraint.c) + * command-line, and fdisk-like frontend (parted/*) + * stubs for ntfs, hfs, xfs + * hacked on libparted/fs_ext2 a fair bit (bug fixes, error handling, + support for > 1024 groups, etc.) + * libparted/mbr.s (the MBR boot loader code) + * misc hacking on GNU/Hurd port + * major surgery on GUID Partition Table (GPT) support (disk_gpt.[ch]) + * progress meter support (libparted/timer.c include/parted/timer.h) + + Snail mail: + 18 Shaw St + Ashwood, 3147 + Australia + +Lennert Buytenhek <buytenh@gnu.org> + * original ext2 code (libparted/fs_ext2) + * discussion/ideas for API + +Matthew Wilson <msw@redhat.com> + * basis of partition table and device code (disk.c, disk_dos.c, + and device.c) Has morphed into something that looks completely + different now :-) + * bug fixes + * BSD disklabel support (disk_bsd.c) + * Python bindings to libparted + * Don't detect AIX physical volumes as msdos partition tables + * Code for manipulating AIX PVs + +Martin von Löwis <martin@mira.isdn.cs.tu-berlin.de> + * German translations + +Baty Jean-Marc <baty@club-internet.fr> + * French translations + +Hiroshi Takekawa <takekawa@sr3.t.u-tokyo.ac.jp> + * Japanese translations + +Eliphas Levy Theodoro <eliphas@conectiva.com> + * Brazillian Portugese translations + +Dmitry S. Sivachenko <dima@Chg.RU> + * Russian translations + +Timshel Knoll <timshel@pobox.com> + * man pages (parted.8 partprobe.8) + * bug fixes + +Ivo Timmermans <itimmermans@bigfoot.com> + * Dutch translations + +Ryoji Kawagishi <kawagisi@yk.rim.or.jp> + * Japanese translation of doc/USER (user documentation) + (replaced by Okuji's version, now) + +Okuji Yoshinori <okuji@kuicr.kyoto-a.ac.jp> + * doc/USER.jp + * contributions to Japanese translations + +Masahiro Sakai <ZVM01052@nifty.ne.jp> + * most of the PC98 support (disk_pc98.c), with Andrew Clausen + * lots of tedious work on parted/strlist.c + +Damien Genet <damien.genet@free.fr> + * parted.m4 + +Ben Collins <bcollins@debian.org> + * Sun disk label support (libparted/disk_sun.c) + * stubs for UFS + +Vincent Stelhé <vincent.stelhe@free.fr> + * move syntax patch (make END specification optional). Trivial + for copyright purposes (no disclaimer needed) + +Neal H Walfield <neal@cs.uml.edu> + * GNU/Hurd port - libparted/gnu.c + +Thomas Roelz <tom@suse.de> + * misc bug fixes + +Matt Domsch <Matt_Domsch@dell.com> + * GUID Partition Table (GPT) support (disk_gpt.[ch], + crc32.[ch]) + * misc bug fixes, including end-of-device workaround + in libparted/linux.c + +Kjetil Torgrim Homme <kjetilho@linpro.no> + * Norweigen translations + +Jörgen Tegnér <jorgen.tegner@telia.com> + * Swedish translations + +Keld Simonsen <keld@dkuug.dk> + * Danish translations + +Richard M. Kreuter <kreuter@ausar.rutgers.edu> + * converted doc/USER to texinfo (doc/parted.texi) + +Miquel Matas <miquelmatas@wanadoo.es> + * Catalan translations + +Andreas Dilger <adilger@clusterfs.com> + * lots of mix bug fixes/cleanups + +Vicente E. Llorens <vllorens@mundofree.com> + * Spanish translations + +Yury Umanets <torque@ukrpost.net> + * basis of libparted/fs_reiserfs + +Bernardo João Torres da Silveira + <bernardojts@ig.com.br> + * pt_BR translation of FAQ and parted.texi + +Wojciech Polak <polak@gnu.org> + * Polish translations + +Miloslav Trmac <mitr@volny.cz> + * Czech translations + +Maxim V. Dziumanenko <mvd@mylinux.com.ua> + * Ukrainian translations + +Giuseppe Sacco <eppesuig@debian.org> + * Italian translations + +Guillaume Knispel <k_guillaume@libertysurf.fr> + * nearly all hfs and hfs+ code (libparted/fs_hfs) + * hfs+ support for mac partitions (disk_mac.c) + * sync_fast code (linux.c gnu.c geom.[ch] device.[ch] ) + * various fixes (parted.c ui.c filesys.c disk_dos.c disk.c + doc/parted.texi doc/API disk_gpt.c disk_mac.c unit.c fs_fat/traverse.c) + +Chris Lumens <clumens@redhat.com> + * interactive help fixes for filesystem types + * gcc-4 pedanticism cleanups + +Wei-Lun Chao <chaoweilun@pcmail.com.tw> + * Taiwanese dialect of Chinese (Cantonese?) translations. + +Tran Thi Hoang Quyen <banhdauxanhhaiduong@gmail.com> + * Vietnamese translations + +Eduardo Maestri Righes <eduardo@tteng.com.br> + * hidden partitions support + * setting MS Reserved partitions through "set" command. + +Arif E. Nugroho <arif_endro@yahoo.com> + * Indonesian translations + +Ithamar R. Adema <ithamar@unet.nl> + * BeOS support + +David Cantrell <dcantrell@redhat.com> + * Added support for Promise SX8 devices (original patch from Jeremy + Katz <katzj@redhat.com>) + * Brought IBM/Red Hat DASD patches forward to parted-1.7.x + Original IBM authors: Volker Sameske <sameske@de.ibm.com> + Horst Hummel <Horst.Hummel@de.ibm.com> + Original Red Hat authors: Phil Knirsch <phil@redhat.de> + Harald Hoyer <harald@redhat.de> + * Prevent SIGFPE when FAT sector size is 0 + * Add virtual DASD (VIODASD) on iSeries support + * Use O_DIRECT I/O to prevent first partition corruption on GPT disks + * Prevent sector reading exceptions from ped_geometry_check() on Mac + disklabels. + * Various bug fixes and other things. + +Peter Jones <pjones@redhat.com> + * Add ped_exception_get_handler() + * /dev/mapper read/write support on Linux (via libdevmapper) + +Darren Lavender <dl1@hppine99.gbr.hp.com> + * Fix SIGSEGV in parted 1.6.19 and assertion error in 1.7.0 + * Add support for LUN/device resize detection and option GPT + header corruption + * Fixed off-by-one error in GPT header that allowed for overlap + between LDAs of LastUsableLBA and PartitionEntryLBA in backup GPT + +Olaf Hering <olh@suse.de> + * Fixes for Macintosh disk label code + +Debarshi Ray <rishi@gnu.org> + * Display disk model and transport type in parted(8). + * '--list/-l' command line switch. + * Introduce 'print devices' and alias 'print list' to 'print all'. + * Alias 'mktable' to 'mklabel'. + * Code and API clean-up, bug fixes, and other miscellaneous stuff. + +Harald Welte <laforge@gnumonks.org> + * SD/MMC Storage card support on Linux @@ -0,0 +1,675 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +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: + + <program> Copyright (C) <year> <name of author> + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. @@ -0,0 +1,18 @@ + +This source tree is a hack job stealing code from the latests parted release +still having filesystem support. The reason for that is that the resizing +capabilities for fat and hfs were quite uniqueu and needed rescuing. + +cmd/ contains a simple command line tool to resize filesystem, the rest is +copy & pasted from parted and gnulib, and needs a major trim given that the +tool really just acts on Linux block device nodes, and doesn't care about +the concept of disks or partition labels. + +Due to the lack of a proper build system the first the libparted stub needs +to be built by doing a make in the libparted subdirectory, and after that +the tool by doing a make in the cmd directory. + +TODO: + - add a real build system + - cut down the amount of stolen libparted code + - check what the canonical way to include gnulib is, or just remove its use diff --git a/cmd/.gitignore b/cmd/.gitignore new file mode 100644 index 0000000..8da0d59 --- /dev/null +++ b/cmd/.gitignore @@ -0,0 +1 @@ +parted diff --git a/cmd/Makefile b/cmd/Makefile new file mode 100644 index 0000000..8c67b17 --- /dev/null +++ b/cmd/Makefile @@ -0,0 +1,17 @@ + +CFLAGS+= -I../include -Wall -O2 + +OBJS= cmd.o \ + ../gnulib/xmalloc.o ../gnulib/xalloc-die.o ../gnulib/basename-lgpl.o \ + ../gnulib/exitfail.o ../gnulib/xstrtoll.o ../gnulib/closeout.o \ + ../gnulib/close-stream.o ../gnulib/progname.o ../gnulib/version-etc.o \ + ../gnulib/version-etc-fsf.o ../gnulib/argmatch.o \ + ../gnulib/quote.o ../gnulib/quotearg.o + +fsresize: $(OBJS) + $(CC) -o fsresize $(OBJS) -lncurses -lreadline -luuid ../libparted/libparted.a + +all: fsresize + +clean: + rm -f $(OBJS) parted-fs diff --git a/cmd/cmd.c b/cmd/cmd.c new file mode 100644 index 0000000..b392e9b --- /dev/null +++ b/cmd/cmd.c @@ -0,0 +1,80 @@ + +#include <config.h> +#include <stdbool.h> + +#include "argmatch.h" +#include "closeout.h" +#include "configmake.h" +#include "version-etc.h" +#include "progname.h" +#include "version.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include "xalloc.h" + +int main(int argc, char **argv) +{ + PedSector start = 0, len = 0; + PedGeometry geom, new_geom; + PedDevice *dev; + PedFileSystem *fs; + PedTimer *g_timer = NULL; + + if (argc != 2 && argc != 4) { + fprintf(stderr, "usage: %s <device>\n" + " %s <device> <start> <length>\n", + argv[0], argv[0]); + return 1; + } + + dev = ped_device_get(argv[1]); + if (!dev) { + fprintf(stderr, "cannot create device %s\n", argv[1]); + return 1; + } + + if (!ped_device_open(dev)) { + fprintf(stderr, "cannot open device %s\n", argv[1]); + return 1; + } + + if (!ped_geometry_init(&geom, dev, 0, dev->length)) { + fprintf(stderr, "cannot initialize geometry\n"); + return 1; + } + + if (argc > 2) { + start = strtoll(argv[2], NULL, 0); + len = strtoll(argv[3], NULL, 0); + } else { + start = 0; + len = dev->length; + } + + if (!ped_geometry_init(&new_geom, dev, start, len)) { + fprintf(stderr, "cannot initialize new geometry\n"); + return 1; + } + + fs = ped_file_system_open(&geom); + if (!fs) { + fprintf(stderr, "cannot read fs\n"); + return 1; + } + + if (!ped_file_system_resize(fs, &new_geom, g_timer)) { + fprintf(stderr, "cannot resize filesystem\n"); + return 1; + } + + ped_file_system_close(fs); + return 0; +} diff --git a/gnulib/argmatch.c b/gnulib/argmatch.c new file mode 100644 index 0000000..9a3eca4 --- /dev/null +++ b/gnulib/argmatch.c @@ -0,0 +1,277 @@ +/* argmatch.c -- find a match for a string in an array + + Copyright (C) 1990, 1998-1999, 2001-2007, 2009-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by David MacKenzie <djm@ai.mit.edu> + Modified by Akim Demaille <demaille@inf.enst.fr> */ + +#include <config.h> + +/* Specification. */ +#include "argmatch.h" + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#include "error.h" +#include "quotearg.h" +#include "quote.h" + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +/* When reporting an invalid argument, show nonprinting characters + by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use + literal_quoting_style. */ +#ifndef ARGMATCH_QUOTING_STYLE +# define ARGMATCH_QUOTING_STYLE locale_quoting_style +#endif + +/* Non failing version of argmatch call this function after failing. */ +#ifndef ARGMATCH_DIE +# include "exitfail.h" +# define ARGMATCH_DIE exit (exit_failure) +#endif + +#ifdef ARGMATCH_DIE_DECL +ARGMATCH_DIE_DECL; +#endif + +static void +__argmatch_die (void) +{ + ARGMATCH_DIE; +} + +/* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h. + Default to __argmatch_die, but allow caller to change this at run-time. */ +argmatch_exit_fn argmatch_die = __argmatch_die; + + +/* If ARG is an unambiguous match for an element of the + NULL-terminated array ARGLIST, return the index in ARGLIST + of the matched element, else -1 if it does not match any element + or -2 if it is ambiguous (is a prefix of more than one element). + + If VALLIST is none null, use it to resolve ambiguities limited to + synonyms, i.e., for + "yes", "yop" -> 0 + "no", "nope" -> 1 + "y" is a valid argument, for `0', and "n" for `1'. */ + +ptrdiff_t +argmatch (const char *arg, const char *const *arglist, + const char *vallist, size_t valsize) +{ + size_t i; /* Temporary index in ARGLIST. */ + size_t arglen; /* Length of ARG. */ + ptrdiff_t matchind = -1; /* Index of first nonexact match. */ + bool ambiguous = false; /* If true, multiple nonexact match(es). */ + + arglen = strlen (arg); + + /* Test all elements for either exact match or abbreviated matches. */ + for (i = 0; arglist[i]; i++) + { + if (!strncmp (arglist[i], arg, arglen)) + { + if (strlen (arglist[i]) == arglen) + /* Exact match found. */ + return i; + else if (matchind == -1) + /* First nonexact match found. */ + matchind = i; + else + { + /* Second nonexact match found. */ + if (vallist == NULL + || memcmp (vallist + valsize * matchind, + vallist + valsize * i, valsize)) + { + /* There is a real ambiguity, or we could not + disambiguate. */ + ambiguous = true; + } + } + } + } + if (ambiguous) + return -2; + else + return matchind; +} + +/* Error reporting for argmatch. + CONTEXT is a description of the type of entity that was being matched. + VALUE is the invalid value that was given. + PROBLEM is the return value from argmatch. */ + +void +argmatch_invalid (const char *context, const char *value, ptrdiff_t problem) +{ + char const *format = (problem == -1 + ? _("invalid argument %s for %s") + : _("ambiguous argument %s for %s")); + + error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value), + quote_n (1, context)); +} + +/* List the valid arguments for argmatch. + ARGLIST is the same as in argmatch. + VALLIST is a pointer to an array of values. + VALSIZE is the size of the elements of VALLIST */ +void +argmatch_valid (const char *const *arglist, + const char *vallist, size_t valsize) +{ + size_t i; + const char *last_val = NULL; + + /* We try to put synonyms on the same line. The assumption is that + synonyms follow each other */ + fprintf (stderr, _("Valid arguments are:")); + for (i = 0; arglist[i]; i++) + if ((i == 0) + || memcmp (last_val, vallist + valsize * i, valsize)) + { + fprintf (stderr, "\n - `%s'", arglist[i]); + last_val = vallist + valsize * i; + } + else + { + fprintf (stderr, ", `%s'", arglist[i]); + } + putc ('\n', stderr); +} + +/* Never failing versions of the previous functions. + + CONTEXT is the context for which argmatch is called (e.g., + "--version-control", or "$VERSION_CONTROL" etc.). Upon failure, + calls the (supposed never to return) function EXIT_FN. */ + +ptrdiff_t +__xargmatch_internal (const char *context, + const char *arg, const char *const *arglist, + const char *vallist, size_t valsize, + argmatch_exit_fn exit_fn) +{ + ptrdiff_t res = argmatch (arg, arglist, vallist, valsize); + if (res >= 0) + /* Success. */ + return res; + + /* We failed. Explain why. */ + argmatch_invalid (context, arg, res); + argmatch_valid (arglist, vallist, valsize); + (*exit_fn) (); + + return -1; /* To please the compilers. */ +} + +/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and + return the first corresponding argument in ARGLIST */ +const char * +argmatch_to_argument (const char *value, + const char *const *arglist, + const char *vallist, size_t valsize) +{ + size_t i; + + for (i = 0; arglist[i]; i++) + if (!memcmp (value, vallist + valsize * i, valsize)) + return arglist[i]; + return NULL; +} + +#ifdef TEST +/* + * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu> + */ +char *program_name; + +/* When to make backup files. */ +enum backup_type +{ + /* Never make backups. */ + no_backups, + + /* Make simple backups of every file. */ + simple_backups, + + /* Make numbered backups of files that already have numbered backups, + and simple backups of the others. */ + numbered_existing_backups, + + /* Make numbered backups of every file. */ + numbered_backups +}; + +/* Two tables describing arguments (keys) and their corresponding + values */ +static const char *const backup_args[] = +{ + "no", "none", "off", + "simple", "never", + "existing", "nil", + "numbered", "t", + 0 +}; + +static const enum backup_type backup_vals[] = +{ + no_backups, no_backups, no_backups, + simple_backups, simple_backups, + numbered_existing_backups, numbered_existing_backups, + numbered_backups, numbered_backups +}; + +int +main (int argc, const char *const *argv) +{ + const char *cp; + enum backup_type backup_type = no_backups; + + program_name = (char *) argv[0]; + + if (argc > 2) + { + fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name); + exit (1); + } + + if ((cp = getenv ("VERSION_CONTROL"))) + backup_type = XARGMATCH ("$VERSION_CONTROL", cp, + backup_args, backup_vals); + + if (argc == 2) + backup_type = XARGMATCH (program_name, argv[1], + backup_args, backup_vals); + + printf ("The version control is `%s'\n", + ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals)); + + return 0; +} +#endif diff --git a/gnulib/basename-lgpl.c b/gnulib/basename-lgpl.c new file mode 100644 index 0000000..529bc35 --- /dev/null +++ b/gnulib/basename-lgpl.c @@ -0,0 +1,75 @@ +/* basename.c -- return the last element in a file name + + Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "dirname.h" + +#include <string.h> + +/* Return the address of the last file name component of NAME. If + NAME has no relative file name components because it is a file + system root, return the empty string. */ + +char * +last_component (char const *name) +{ + char const *base = name + FILE_SYSTEM_PREFIX_LEN (name); + char const *p; + bool saw_slash = false; + + while (ISSLASH (*base)) + base++; + + for (p = base; *p; p++) + { + if (ISSLASH (*p)) + saw_slash = true; + else if (saw_slash) + { + base = p; + saw_slash = false; + } + } + + return (char *) base; +} + +/* Return the length of the basename NAME. Typically NAME is the + value returned by base_name or last_component. Act like strlen + (NAME), except omit all trailing slashes. */ + +size_t +base_len (char const *name) +{ + size_t len; + size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name); + + for (len = strlen (name); 1 < len && ISSLASH (name[len - 1]); len--) + continue; + + if (DOUBLE_SLASH_IS_DISTINCT_ROOT && len == 1 + && ISSLASH (name[0]) && ISSLASH (name[1]) && ! name[2]) + return 2; + + if (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE && prefix_len + && len == prefix_len && ISSLASH (name[prefix_len])) + return prefix_len + 1; + + return len; +} diff --git a/gnulib/close-stream.c b/gnulib/close-stream.c new file mode 100644 index 0000000..bd9c32e --- /dev/null +++ b/gnulib/close-stream.c @@ -0,0 +1,74 @@ +/* Close a stream, with nicer error checking than fclose's. + + Copyright (C) 1998-2002, 2004, 2006-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "close-stream.h" + +#include <errno.h> +#include <stdbool.h> + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +/* Close STREAM. Return 0 if successful, EOF (setting errno) + otherwise. A failure might set errno to 0 if the error number + cannot be determined. + + A failure with errno set to EPIPE may or may not indicate an error + situation worth signaling to the user. See the documentation of the + close_stdout_set_ignore_EPIPE function for details. + + If a program writes *anything* to STREAM, that program should close + STREAM and make sure that it succeeds before exiting. Otherwise, + suppose that you go to the extreme of checking the return status + of every function that does an explicit write to STREAM. The last + printf can succeed in writing to the internal stream buffer, and yet + the fclose(STREAM) could still fail (due e.g., to a disk full error) + when it tries to write out that buffered data. Thus, you would be + left with an incomplete output file and the offending program would + exit successfully. Even calling fflush is not always sufficient, + since some file systems (NFS and CODA) buffer written/flushed data + until an actual close call. + + Besides, it's wasteful to check the return value from every call + that writes to STREAM -- just let the internal stream state record + the failure. That's what the ferror test is checking below. */ + +int +close_stream (FILE *stream) +{ + const bool some_pending = (__fpending (stream) != 0); + const bool prev_fail = (ferror (stream) != 0); + const bool fclose_fail = (fclose (stream) != 0); + + /* Return an error indication if there was a previous failure or if + fclose failed, with one exception: ignore an fclose failure if + there was no previous error, no data remains to be flushed, and + fclose failed with EBADF. That can happen when a program like cp + is invoked like this `cp a b >&-' (i.e., with standard output + closed) and doesn't generate any output (hence no previous error + and nothing to be flushed). */ + + if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) + { + if (! fclose_fail) + errno = 0; + return EOF; + } + + return 0; +} diff --git a/gnulib/close-stream.h b/gnulib/close-stream.h new file mode 100644 index 0000000..be3d419 --- /dev/null +++ b/gnulib/close-stream.h @@ -0,0 +1,2 @@ +#include <stdio.h> +int close_stream (FILE *stream); diff --git a/gnulib/closeout.c b/gnulib/closeout.c new file mode 100644 index 0000000..12b0697 --- /dev/null +++ b/gnulib/closeout.c @@ -0,0 +1,124 @@ +/* Close standard output and standard error, exiting with a diagnostic on error. + + Copyright (C) 1998-2002, 2004, 2006, 2008-2011 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "closeout.h" + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#include "close-stream.h" +#include "error.h" +#include "exitfail.h" +#include "quotearg.h" + +static const char *file_name; + +/* Set the file name to be reported in the event an error is detected + by close_stdout. */ +void +close_stdout_set_file_name (const char *file) +{ + file_name = file; +} + +static bool ignore_EPIPE /* = false */; + +/* Specify the reaction to an EPIPE error during the closing of stdout: + - If ignore = true, it shall be ignored. + - If ignore = false, it shall evoke a diagnostic, along with a nonzero + exit status. + The default is ignore = false. + + This setting matters only if the SIGPIPE signal is ignored (i.e. its + handler set to SIG_IGN) or blocked. Only particular programs need to + temporarily ignore SIGPIPE. If SIGPIPE is ignored or blocked because + it was ignored or blocked in the parent process when it created the + child process, it usually is a bug in the parent process: It is bad + practice to have SIGPIPE ignored or blocked while creating a child + process. + + EPIPE occurs when writing to a pipe or socket that has no readers now, + when SIGPIPE is ignored or blocked. + + The ignore = false setting is suitable for a scenario where it is normally + guaranteed that the pipe writer terminates before the pipe reader. In + this case, an EPIPE is an indication of a premature termination of the + pipe reader and should lead to a diagnostic and a nonzero exit status. + + The ignore = true setting is suitable for a scenario where you don't know + ahead of time whether the pipe writer or the pipe reader will terminate + first. In this case, an EPIPE is an indication that the pipe writer can + stop doing useless write() calls; this is what close_stdout does anyway. + EPIPE is part of the normal pipe/socket shutdown protocol in this case, + and should not lead to a diagnostic message. */ + +void +close_stdout_set_ignore_EPIPE (bool ignore) +{ + ignore_EPIPE = ignore; +} + +/* Close standard output. On error, issue a diagnostic and _exit + with status 'exit_failure'. + + Also close standard error. On error, _exit with status 'exit_failure'. + + Since close_stdout is commonly registered via 'atexit', POSIX + and the C standard both say that it should not call 'exit', + because the behavior is undefined if 'exit' is called more than + once. So it calls '_exit' instead of 'exit'. If close_stdout + is registered via atexit before other functions are registered, + the other functions can act before this _exit is invoked. + + Applications that use close_stdout should flush any streams + other than stdout and stderr before exiting, since the call to + _exit will bypass other buffer flushing. Applications should + be flushing and closing other streams anyway, to check for I/O + errors. Also, applications should not use tmpfile, since _exit + can bypass the removal of these files. + + It's important to detect such failures and exit nonzero because many + tools (most notably `make' and other build-management systems) depend + on being able to detect failure in other tools via their exit status. */ + +void +close_stdout (void) +{ + if (close_stream (stdout) != 0 + && !(ignore_EPIPE && errno == EPIPE)) + { + char const *write_error = _("write error"); + if (file_name) + error (0, errno, "%s: %s", file_name, + write_error); + else + error (0, errno, "%s", write_error); + + _exit (exit_failure); + } + + if (close_stream (stderr) != 0) + _exit (exit_failure); +} diff --git a/gnulib/exitfail.c b/gnulib/exitfail.c new file mode 100644 index 0000000..953aa02 --- /dev/null +++ b/gnulib/exitfail.c @@ -0,0 +1,24 @@ +/* Failure exit status + + Copyright (C) 2002-2003, 2005-2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "exitfail.h" + +#include <stdlib.h> + +int volatile exit_failure = EXIT_FAILURE; diff --git a/gnulib/exitfail.h b/gnulib/exitfail.h new file mode 100644 index 0000000..6d01d4f --- /dev/null +++ b/gnulib/exitfail.h @@ -0,0 +1,18 @@ +/* Failure exit status + + Copyright (C) 2002, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +extern int volatile exit_failure; diff --git a/gnulib/gettext.h b/gnulib/gettext.h new file mode 100644 index 0000000..65777e6 --- /dev/null +++ b/gnulib/gettext.h @@ -0,0 +1,286 @@ +/* Convenience header for conditional use of GNU <libintl.h>. + Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2011 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include <libintl.h> + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of <locale.h> a NOP. We don't include <libintl.h> + as well because people using "gettext.h" will not include <libintl.h>, + and also including <libintl.h> would fail on SunOS 4, whereas <locale.h> + is OK. */ +#if defined(__sun) +# include <locale.h> +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + <libintl.h>, which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of <libintl.h> a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include <cstdlib> +# if (__GLIBC__ >= 2 && !defined __UCLIBC__) || _GLIBCXX_HAVE_LIBINTL_H +# include <libintl.h> +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# undef gettext +# define gettext(Msgid) ((const char *) (Msgid)) +# undef dgettext +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# undef dcgettext +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# undef dngettext +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# undef dcngettext +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext (Domainname, Msgid1, Msgid2, N)) +# undef textdomain +# define textdomain(Domainname) ((const char *) (Domainname)) +# undef bindtextdomain +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# undef bind_textdomain_codeset +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* Prefer gnulib's setlocale override over libintl's setlocale override. */ +#ifdef GNULIB_defined_setlocale +# undef setlocale +# define setlocale rpl_setlocale +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include <string.h> + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include <stdlib.h> +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/gnulib/intprops.h b/gnulib/intprops.h new file mode 100644 index 0000000..d722648 --- /dev/null +++ b/gnulib/intprops.h @@ -0,0 +1,320 @@ +/* intprops.h -- properties of integer types + + Copyright (C) 2001-2005, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert. */ + +#ifndef _GL_INTPROPS_H +#define _GL_INTPROPS_H + +#include <limits.h> + +/* Return a integer value, converted to the same type as the integer + expression E after integer type promotion. V is the unconverted value. + E should not have side effects. */ +#define _GL_INT_CONVERT(e, v) (0 * (e) + (v)) + +/* Act like _GL_INT_CONVERT (E, -V) but work around a bug in IRIX 6.5 cc; see + <http://lists.gnu.org/archive/html/bug-gnulib/2011-05/msg00406.html>. */ +#define _GL_INT_NEGATE_CONVERT(e, v) (0 * (e) - (v)) + +/* The extra casts in the following macros work around compiler bugs, + e.g., in Cray C 5.0.3.0. */ + +/* True if the arithmetic type T is an integer type. bool counts as + an integer. */ +#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1) + +/* True if negative values of the signed integer type T use two's + complement, ones' complement, or signed magnitude representation, + respectively. Much GNU code assumes two's complement, but some + people like to be portable to all possible C hosts. */ +#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) +#define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0) +#define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1) + +/* True if the signed integer expression E uses two's complement. */ +#define _GL_INT_TWOS_COMPLEMENT(e) (~ _GL_INT_CONVERT (e, 0) == -1) + +/* True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* Return 1 if the integer expression E, after integer promotion, has + a signed type. E should not have side effects. */ +#define _GL_INT_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0) + + +/* Minimum and maximum values for integer types and expressions. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ + +/* The maximum and minimum values for the integer type T. */ +#define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : TYPE_SIGNED_MAGNITUDE (t) \ + ? ~ (t) 0 \ + : ~ TYPE_MAXIMUM (t))) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) + +/* The maximum and minimum values for the type of the expression E, + after integer promotion. E should not have side effects. */ +#define _GL_INT_MINIMUM(e) \ + (_GL_INT_SIGNED (e) \ + ? - _GL_INT_TWOS_COMPLEMENT (e) - _GL_SIGNED_INT_MAXIMUM (e) \ + : _GL_INT_CONVERT (e, 0)) +#define _GL_INT_MAXIMUM(e) \ + (_GL_INT_SIGNED (e) \ + ? _GL_SIGNED_INT_MAXIMUM (e) \ + : _GL_INT_NEGATE_CONVERT (e, 1)) +#define _GL_SIGNED_INT_MAXIMUM(e) \ + (((_GL_INT_CONVERT (e, 1) << (sizeof ((e) + 0) * CHAR_BIT - 2)) - 1) * 2 + 1) + + +/* Return 1 if the __typeof__ keyword works. This could be done by + 'configure', but for now it's easier to do it by hand. */ +#if 2 <= __GNUC__ || 0x5110 <= __SUNPRO_C +# define _GL_HAVE___TYPEOF__ 1 +#else +# define _GL_HAVE___TYPEOF__ 0 +#endif + +/* Return 1 if the integer type or expression T might be signed. Return 0 + if it is definitely unsigned. This macro does not evaluate its argument, + and expands to an integer constant expression. */ +#if _GL_HAVE___TYPEOF__ +# define _GL_SIGNED_TYPE_OR_EXPR(t) TYPE_SIGNED (__typeof__ (t)) +#else +# define _GL_SIGNED_TYPE_OR_EXPR(t) 1 +#endif + +/* Bound on length of the string representing an unsigned integer + value representable in B bits. log10 (2.0) < 146/485. The + smallest value of B where this bound is not tight is 2621. */ +#define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485) + +/* Bound on length of the string representing an integer type or expression T. + Subtract 1 for the sign bit if T is signed, and then add 1 more for + a minus sign if needed. + + Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 0 when its argument is + signed, this macro may overestimate the true bound by one byte when + applied to unsigned types of size 2, 4, 16, ... bytes. */ +#define INT_STRLEN_BOUND(t) \ + (INT_BITS_STRLEN_BOUND (sizeof (t) * CHAR_BIT \ + - _GL_SIGNED_TYPE_OR_EXPR (t)) \ + + _GL_SIGNED_TYPE_OR_EXPR (t)) + +/* Bound on buffer size needed to represent an integer type or expression T, + including the terminating null. */ +#define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1) + + +/* Range overflow checks. + + The INT_<op>_RANGE_OVERFLOW macros return 1 if the corresponding C + operators might not yield numerically correct answers due to + arithmetic overflow. They do not rely on undefined or + implementation-defined behavior. Their implementations are simple + and straightforward, but they are a bit harder to use than the + INT_<op>_OVERFLOW macros described below. + + Example usage: + + long int i = ...; + long int j = ...; + if (INT_MULTIPLY_RANGE_OVERFLOW (i, j, LONG_MIN, LONG_MAX)) + printf ("multiply would overflow"); + else + printf ("product is %ld", i * j); + + Restrictions on *_RANGE_OVERFLOW macros: + + These macros do not check for all possible numerical problems or + undefined or unspecified behavior: they do not check for division + by zero, for bad shift counts, or for shifting negative numbers. + + These macros may evaluate their arguments zero or multiple times, + so the arguments should not have side effects. The arithmetic + arguments (including the MIN and MAX arguments) must be of the same + integer type after the usual arithmetic conversions, and the type + must have minimum value MIN and maximum MAX. Unsigned types should + use a zero MIN of the proper type. + + These macros are tuned for constant MIN and MAX. For commutative + operations such as A + B, they are also tuned for constant B. */ + +/* Return 1 if A + B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. */ +#define INT_ADD_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? (a) < (min) - (b) \ + : (max) - (b) < (a)) + +/* Return 1 if A - B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. */ +#define INT_SUBTRACT_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? (max) + (b) < (a) \ + : (a) < (min) + (b)) + +/* Return 1 if - A would overflow in [MIN,MAX] arithmetic. + See above for restrictions. */ +#define INT_NEGATE_RANGE_OVERFLOW(a, min, max) \ + ((min) < 0 \ + ? (a) < - (max) \ + : 0 < (a)) + +/* Return 1 if A * B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Avoid && and || as they tickle + bugs in Sun C 5.11 2010/08/13 and other compilers; see + <http://lists.gnu.org/archive/html/bug-gnulib/2011-05/msg00401.html>. */ +#define INT_MULTIPLY_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? ((a) < 0 \ + ? (a) < (max) / (b) \ + : (b) == -1 \ + ? 0 \ + : (min) / (b) < (a)) \ + : (b) == 0 \ + ? 0 \ + : ((a) < 0 \ + ? (a) < (min) / (b) \ + : (max) / (b) < (a))) + +/* Return 1 if A / B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Do not check for division by zero. */ +#define INT_DIVIDE_RANGE_OVERFLOW(a, b, min, max) \ + ((min) < 0 && (b) == -1 && (a) < - (max)) + +/* Return 1 if A % B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Do not check for division by zero. + Mathematically, % should never overflow, but on x86-like hosts + INT_MIN % -1 traps, and the C standard permits this, so treat this + as an overflow too. */ +#define INT_REMAINDER_RANGE_OVERFLOW(a, b, min, max) \ + INT_DIVIDE_RANGE_OVERFLOW (a, b, min, max) + +/* Return 1 if A << B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Here, MIN and MAX are for A only, and B need + not be of the same type as the other arguments. The C standard says that + behavior is undefined for shifts unless 0 <= B < wordwidth, and that when + A is negative then A << B has undefined behavior and A >> B has + implementation-defined behavior, but do not check these other + restrictions. */ +#define INT_LEFT_SHIFT_RANGE_OVERFLOW(a, b, min, max) \ + ((a) < 0 \ + ? (a) < (min) >> (b) \ + : (max) >> (b) < (a)) + + +/* The _GL*_OVERFLOW macros have the same restrictions as the + *_RANGE_OVERFLOW macros, except that they do not assume that operands + (e.g., A and B) have the same type as MIN and MAX. Instead, they assume + that the result (e.g., A + B) has that type. */ +#define _GL_ADD_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? INT_ADD_RANGE_OVERFLOW (a, b, min, max) \ + : (a) < 0 ? (b) <= (a) + (b) \ + : (b) < 0 ? (a) <= (a) + (b) \ + : (a) + (b) < (b)) +#define _GL_SUBTRACT_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? INT_SUBTRACT_RANGE_OVERFLOW (a, b, min, max) \ + : (a) < 0 ? 1 \ + : (b) < 0 ? (a) - (b) <= (a) \ + : (a) < (b)) +#define _GL_MULTIPLY_OVERFLOW(a, b, min, max) \ + (((min) == 0 && (((a) < 0 && 0 < (b)) || ((b) < 0 && 0 < (a)))) \ + || INT_MULTIPLY_RANGE_OVERFLOW (a, b, min, max)) +#define _GL_DIVIDE_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? (b) == _GL_INT_NEGATE_CONVERT (min, 1) && (a) < - (max) \ + : (a) < 0 ? (b) <= (a) + (b) - 1 \ + : (b) < 0 && (a) + (b) <= (a)) +#define _GL_REMAINDER_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? (b) == _GL_INT_NEGATE_CONVERT (min, 1) && (a) < - (max) \ + : (a) < 0 ? (a) % (b) != ((max) - (b) + 1) % (b) \ + : (b) < 0 && ! _GL_UNSIGNED_NEG_MULTIPLE (a, b, max)) + +/* Return a nonzero value if A is a mathematical multiple of B, where + A is unsigned, B is negative, and MAX is the maximum value of A's + type. A's type must be the same as (A % B)'s type. Normally (A % + -B == 0) suffices, but things get tricky if -B would overflow. */ +#define _GL_UNSIGNED_NEG_MULTIPLE(a, b, max) \ + (((b) < -_GL_SIGNED_INT_MAXIMUM (b) \ + ? (_GL_SIGNED_INT_MAXIMUM (b) == (max) \ + ? (a) \ + : (a) % (_GL_INT_CONVERT (a, _GL_SIGNED_INT_MAXIMUM (b)) + 1)) \ + : (a) % - (b)) \ + == 0) + + +/* Integer overflow checks. + + The INT_<op>_OVERFLOW macros return 1 if the corresponding C operators + might not yield numerically correct answers due to arithmetic overflow. + They work correctly on all known practical hosts, and do not rely + on undefined behavior due to signed arithmetic overflow. + + Example usage: + + long int i = ...; + long int j = ...; + if (INT_MULTIPLY_OVERFLOW (i, j)) + printf ("multiply would overflow"); + else + printf ("product is %ld", i * j); + + These macros do not check for all possible numerical problems or + undefined or unspecified behavior: they do not check for division + by zero, for bad shift counts, or for shifting negative numbers. + + These macros may evaluate their arguments zero or multiple times, so the + arguments should not have side effects. + + These macros are tuned for their last argument being a constant. + + Return 1 if the integer expressions A * B, A - B, -A, A * B, A / B, + A % B, and A << B would overflow, respectively. */ + +#define INT_ADD_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_ADD_OVERFLOW) +#define INT_SUBTRACT_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_SUBTRACT_OVERFLOW) +#define INT_NEGATE_OVERFLOW(a) \ + INT_NEGATE_RANGE_OVERFLOW (a, _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a)) +#define INT_MULTIPLY_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_MULTIPLY_OVERFLOW) +#define INT_DIVIDE_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_DIVIDE_OVERFLOW) +#define INT_REMAINDER_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_REMAINDER_OVERFLOW) +#define INT_LEFT_SHIFT_OVERFLOW(a, b) \ + INT_LEFT_SHIFT_RANGE_OVERFLOW (a, b, \ + _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a)) + +/* Return 1 if the expression A <op> B would overflow, + where OP_RESULT_OVERFLOW (A, B, MIN, MAX) does the actual test, + assuming MIN and MAX are the minimum and maximum for the result type. + Arguments should be free of side effects. */ +#define _GL_BINARY_OP_OVERFLOW(a, b, op_result_overflow) \ + op_result_overflow (a, b, \ + _GL_INT_MINIMUM (0 * (b) + (a)), \ + _GL_INT_MAXIMUM (0 * (b) + (a))) + +#endif /* _GL_INTPROPS_H */ diff --git a/gnulib/progname.c b/gnulib/progname.c new file mode 100644 index 0000000..2465748 --- /dev/null +++ b/gnulib/progname.c @@ -0,0 +1,92 @@ +/* Program name management. + Copyright (C) 2001-2003, 2005-2011 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2001. + + 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 <http://www.gnu.org/licenses/>. */ + + +#include <config.h> + +/* Specification. */ +#undef ENABLE_RELOCATABLE /* avoid defining set_program_name as a macro */ +#include "progname.h" + +#include <errno.h> /* get program_invocation_name declaration */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/* String containing name the program is called with. + To be initialized by main(). */ +const char *program_name = NULL; + +/* Set program_name, based on argv[0]. + argv0 must be a string allocated with indefinite extent, and must not be + modified after this call. */ +void +set_program_name (const char *argv0) +{ + /* libtool creates a temporary executable whose name is sometimes prefixed + with "lt-" (depends on the platform). It also makes argv[0] absolute. + But the name of the temporary executable is a detail that should not be + visible to the end user and to the test suite. + Remove this "<dirname>/.libs/" or "<dirname>/.libs/lt-" prefix here. */ + const char *slash; + const char *base; + + /* Sanity check. POSIX requires the invoking process to pass a non-NULL + argv[0]. */ + if (argv0 == NULL) + { + /* It's a bug in the invoking program. Help diagnosing it. */ + fputs ("A NULL argv[0] was passed through an exec system call.\n", + stderr); + abort (); + } + + slash = strrchr (argv0, '/'); + base = (slash != NULL ? slash + 1 : argv0); + if (base - argv0 >= 7 && strncmp (base - 7, "/.libs/", 7) == 0) + { + argv0 = base; + if (strncmp (base, "lt-", 3) == 0) + { + argv0 = base + 3; + /* On glibc systems, remove the "lt-" prefix from the variable + program_invocation_short_name. */ +#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + program_invocation_short_name = (char *) argv0; +#endif + } + } + + /* But don't strip off a leading <dirname>/ in general, because when the user + runs + /some/hidden/place/bin/cp foo foo + he should get the error message + /some/hidden/place/bin/cp: `foo' and `foo' are the same file + not + cp: `foo' and `foo' are the same file + */ + + program_name = argv0; + + /* On glibc systems, the error() function comes from libc and uses the + variable program_invocation_name, not program_name. So set this variable + as well. */ +#if HAVE_DECL_PROGRAM_INVOCATION_NAME + program_invocation_name = (char *) argv0; +#endif +} diff --git a/gnulib/quote.c b/gnulib/quote.c new file mode 100644 index 0000000..1989c8c --- /dev/null +++ b/gnulib/quote.c @@ -0,0 +1,40 @@ +/* quote.c - quote arguments for output + + Copyright (C) 1998-2001, 2003, 2005-2006, 2009-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert <eggert@twinsun.com> */ + +#include <config.h> + +#include "quotearg.h" +#include "quote.h" + +/* Return an unambiguous printable representation of NAME, + allocated in slot N, suitable for diagnostics. */ +char const * +quote_n (int n, char const *name) +{ + return quotearg_n_style (n, locale_quoting_style, name); +} + +/* Return an unambiguous printable representation of NAME, + suitable for diagnostics. */ +char const * +quote (char const *name) +{ + return quote_n (0, name); +} diff --git a/gnulib/quote.h b/gnulib/quote.h new file mode 100644 index 0000000..d0acb51 --- /dev/null +++ b/gnulib/quote.h @@ -0,0 +1,20 @@ +/* quote.h - prototypes for quote.c + + Copyright (C) 1998-2001, 2003, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + + +char const *quote_n (int n, char const *name); +char const *quote (char const *name); diff --git a/gnulib/quotearg.c b/gnulib/quotearg.c new file mode 100644 index 0000000..da8ba1e --- /dev/null +++ b/gnulib/quotearg.c @@ -0,0 +1,888 @@ +/* quotearg.c - quote arguments for output + + Copyright (C) 1998-2002, 2004-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert <eggert@twinsun.com> */ + +#include <config.h> + +#include "quotearg.h" + +#include "xalloc.h" + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#define INT_BITS (sizeof (int) * CHAR_BIT) + +struct quoting_options +{ + /* Basic quoting style. */ + enum quoting_style style; + + /* Additional flags. Bitwise combination of enum quoting_flags. */ + int flags; + + /* Quote the characters indicated by this bit vector even if the + quoting style would not normally require them to be quoted. */ + unsigned int quote_these_too[(UCHAR_MAX / INT_BITS) + 1]; + + /* The left quote for custom_quoting_style. */ + char const *left_quote; + + /* The right quote for custom_quoting_style. */ + char const *right_quote; +}; + +/* Names of quoting styles. */ +char const *const quoting_style_args[] = +{ + "literal", + "shell", + "shell-always", + "c", + "c-maybe", + "escape", + "locale", + "clocale", + 0 +}; + +/* Correspondences to quoting style names. */ +enum quoting_style const quoting_style_vals[] = +{ + literal_quoting_style, + shell_quoting_style, + shell_always_quoting_style, + c_quoting_style, + c_maybe_quoting_style, + escape_quoting_style, + locale_quoting_style, + clocale_quoting_style +}; + +/* The default quoting options. */ +static struct quoting_options default_quoting_options; + +/* Allocate a new set of quoting options, with contents initially identical + to O if O is not null, or to the default if O is null. + It is the caller's responsibility to free the result. */ +struct quoting_options * +clone_quoting_options (struct quoting_options *o) +{ + int e = errno; + struct quoting_options *p = xmemdup (o ? o : &default_quoting_options, + sizeof *o); + errno = e; + return p; +} + +/* Get the value of O's quoting style. If O is null, use the default. */ +enum quoting_style +get_quoting_style (struct quoting_options *o) +{ + return (o ? o : &default_quoting_options)->style; +} + +/* In O (or in the default if O is null), + set the value of the quoting style to S. */ +void +set_quoting_style (struct quoting_options *o, enum quoting_style s) +{ + (o ? o : &default_quoting_options)->style = s; +} + +/* In O (or in the default if O is null), + set the value of the quoting options for character C to I. + Return the old value. Currently, the only values defined for I are + 0 (the default) and 1 (which means to quote the character even if + it would not otherwise be quoted). */ +int +set_char_quoting (struct quoting_options *o, char c, int i) +{ + unsigned char uc = c; + unsigned int *p = + (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS; + int shift = uc % INT_BITS; + int r = (*p >> shift) & 1; + *p ^= ((i & 1) ^ r) << shift; + return r; +} + +/* In O (or in the default if O is null), + set the value of the quoting options flag to I, which can be a + bitwise combination of enum quoting_flags, or 0 for default + behavior. Return the old value. */ +int +set_quoting_flags (struct quoting_options *o, int i) +{ + int r; + if (!o) + o = &default_quoting_options; + r = o->flags; + o->flags = i; + return r; +} + +void +set_custom_quoting (struct quoting_options *o, + char const *left_quote, char const *right_quote) +{ + if (!o) + o = &default_quoting_options; + o->style = custom_quoting_style; + if (!left_quote || !right_quote) + abort (); + o->left_quote = left_quote; + o->right_quote = right_quote; +} + +/* Return quoting options for STYLE, with no extra quoting. */ +static struct quoting_options +quoting_options_from_style (enum quoting_style style) +{ + struct quoting_options o = { 0 }; + if (style == custom_quoting_style) + abort (); + o.style = style; + return o; +} + +/* MSGID approximates a quotation mark. Return its translation if it + has one; otherwise, return either it or "\"", depending on S. */ +static char const * +gettext_quote (char const *msgid, enum quoting_style s) +{ + char const *translation = _(msgid); + if (translation == msgid && s == clocale_quoting_style) + translation = "\""; + return translation; +} + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using QUOTING_STYLE, FLAGS, and + QUOTE_THESE_TOO to control quoting. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is SIZE_MAX, use the string length of the argument for ARGSIZE. + + This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG, + ARGSIZE, O), except it breaks O into its component pieces and is + not careful about errno. */ + +static size_t +quotearg_buffer_restyled (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + enum quoting_style quoting_style, int flags, + unsigned int const *quote_these_too, + char const *left_quote, + char const *right_quote) +{ + size_t i; + size_t len = 0; + char const *quote_string = 0; + size_t quote_string_len = 0; + bool backslash_escapes = false; + bool unibyte_locale = MB_CUR_MAX == 1; + bool elide_outer_quotes = (flags & QA_ELIDE_OUTER_QUOTES) != 0; + +#define STORE(c) \ + do \ + { \ + if (len < buffersize) \ + buffer[len] = (c); \ + len++; \ + } \ + while (0) + + switch (quoting_style) + { + case c_maybe_quoting_style: + quoting_style = c_quoting_style; + elide_outer_quotes = true; + /* Fall through. */ + case c_quoting_style: + if (!elide_outer_quotes) + STORE ('"'); + backslash_escapes = true; + quote_string = "\""; + quote_string_len = 1; + break; + + case escape_quoting_style: + backslash_escapes = true; + elide_outer_quotes = false; + break; + + case locale_quoting_style: + case clocale_quoting_style: + case custom_quoting_style: + { + if (quoting_style != custom_quoting_style) + { + /* TRANSLATORS: + Get translations for open and closing quotation marks. + + The message catalog should translate "`" to a left + quotation mark suitable for the locale, and similarly for + "'". If the catalog has no translation, + locale_quoting_style quotes `like this', and + clocale_quoting_style quotes "like this". + + For example, an American English Unicode locale should + translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and + should translate "'" to U+201D (RIGHT DOUBLE QUOTATION + MARK). A British English Unicode locale should instead + translate these to U+2018 (LEFT SINGLE QUOTATION MARK) + and U+2019 (RIGHT SINGLE QUOTATION MARK), respectively. + + If you don't know what to put here, please see + <http://en.wikipedia.org/wiki/Quotation_mark#Glyphs> + and use glyphs suitable for your language. */ + left_quote = gettext_quote (N_("`"), quoting_style); + right_quote = gettext_quote (N_("'"), quoting_style); + } + if (!elide_outer_quotes) + for (quote_string = left_quote; *quote_string; quote_string++) + STORE (*quote_string); + backslash_escapes = true; + quote_string = right_quote; + quote_string_len = strlen (quote_string); + } + break; + + case shell_quoting_style: + quoting_style = shell_always_quoting_style; + elide_outer_quotes = true; + /* Fall through. */ + case shell_always_quoting_style: + if (!elide_outer_quotes) + STORE ('\''); + quote_string = "'"; + quote_string_len = 1; + break; + + case literal_quoting_style: + elide_outer_quotes = false; + break; + + default: + abort (); + } + + for (i = 0; ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize); i++) + { + unsigned char c; + unsigned char esc; + bool is_right_quote = false; + + if (backslash_escapes + && quote_string_len + && i + quote_string_len <= argsize + && memcmp (arg + i, quote_string, quote_string_len) == 0) + { + if (elide_outer_quotes) + goto force_outer_quoting_style; + is_right_quote = true; + } + + c = arg[i]; + switch (c) + { + case '\0': + if (backslash_escapes) + { + if (elide_outer_quotes) + goto force_outer_quoting_style; + STORE ('\\'); + /* If quote_string were to begin with digits, we'd need to + test for the end of the arg as well. However, it's + hard to imagine any locale that would use digits in + quotes, and set_custom_quoting is documented not to + accept them. */ + if (i + 1 < argsize && '0' <= arg[i + 1] && arg[i + 1] <= '9') + { + STORE ('0'); + STORE ('0'); + } + c = '0'; + /* We don't have to worry that this last '0' will be + backslash-escaped because, again, quote_string should + not start with it and because quote_these_too is + documented as not accepting it. */ + } + else if (flags & QA_ELIDE_NULL_BYTES) + continue; + break; + + case '?': + switch (quoting_style) + { + case shell_always_quoting_style: + if (elide_outer_quotes) + goto force_outer_quoting_style; + break; + + case c_quoting_style: + if ((flags & QA_SPLIT_TRIGRAPHS) + && i + 2 < argsize && arg[i + 1] == '?') + switch (arg[i + 2]) + { + case '!': case '\'': + case '(': case ')': case '-': case '/': + case '<': case '=': case '>': + /* Escape the second '?' in what would otherwise be + a trigraph. */ + if (elide_outer_quotes) + goto force_outer_quoting_style; + c = arg[i + 2]; + i += 2; + STORE ('?'); + STORE ('"'); + STORE ('"'); + STORE ('?'); + break; + + default: + break; + } + break; + + default: + break; + } + break; + + case '\a': esc = 'a'; goto c_escape; + case '\b': esc = 'b'; goto c_escape; + case '\f': esc = 'f'; goto c_escape; + case '\n': esc = 'n'; goto c_and_shell_escape; + case '\r': esc = 'r'; goto c_and_shell_escape; + case '\t': esc = 't'; goto c_and_shell_escape; + case '\v': esc = 'v'; goto c_escape; + case '\\': esc = c; + /* No need to escape the escape if we are trying to elide + outer quotes and nothing else is problematic. */ + if (backslash_escapes && elide_outer_quotes && quote_string_len) + goto store_c; + + c_and_shell_escape: + if (quoting_style == shell_always_quoting_style + && elide_outer_quotes) + goto force_outer_quoting_style; + /* Fall through. */ + c_escape: + if (backslash_escapes) + { + c = esc; + goto store_escape; + } + break; + + case '{': case '}': /* sometimes special if isolated */ + if (! (argsize == SIZE_MAX ? arg[1] == '\0' : argsize == 1)) + break; + /* Fall through. */ + case '#': case '~': + if (i != 0) + break; + /* Fall through. */ + case ' ': + case '!': /* special in bash */ + case '"': case '$': case '&': + case '(': case ')': case '*': case ';': + case '<': + case '=': /* sometimes special in 0th or (with "set -k") later args */ + case '>': case '[': + case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */ + case '`': case '|': + /* A shell special character. In theory, '$' and '`' could + be the first bytes of multibyte characters, which means + we should check them with mbrtowc, but in practice this + doesn't happen so it's not worth worrying about. */ + if (quoting_style == shell_always_quoting_style + && elide_outer_quotes) + goto force_outer_quoting_style; + break; + + case '\'': + if (quoting_style == shell_always_quoting_style) + { + if (elide_outer_quotes) + goto force_outer_quoting_style; + STORE ('\''); + STORE ('\\'); + STORE ('\''); + } + break; + + case '%': case '+': case ',': case '-': case '.': case '/': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case ':': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': case ']': case '_': case 'a': case 'b': + case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + /* These characters don't cause problems, no matter what the + quoting style is. They cannot start multibyte sequences. + A digit or a special letter would cause trouble if it + appeared at the beginning of quote_string because we'd then + escape by prepending a backslash. However, it's hard to + imagine any locale that would use digits or letters as + quotes, and set_custom_quoting is documented not to accept + them. Also, a digit or a special letter would cause + trouble if it appeared in quote_these_too, but that's also + documented as not accepting them. */ + break; + + default: + /* If we have a multibyte sequence, copy it until we reach + its end, find an error, or come back to the initial shift + state. For C-like styles, if the sequence has + unprintable characters, escape the whole sequence, since + we can't easily escape single characters within it. */ + { + /* Length of multibyte sequence found so far. */ + size_t m; + + bool printable; + + if (unibyte_locale) + { + m = 1; + printable = isprint (c) != 0; + } + else + { + mbstate_t mbstate; + memset (&mbstate, 0, sizeof mbstate); + + m = 0; + printable = true; + if (argsize == SIZE_MAX) + argsize = strlen (arg); + + do + { + wchar_t w; + size_t bytes = mbrtowc (&w, &arg[i + m], + argsize - (i + m), &mbstate); + if (bytes == 0) + break; + else if (bytes == (size_t) -1) + { + printable = false; + break; + } + else if (bytes == (size_t) -2) + { + printable = false; + while (i + m < argsize && arg[i + m]) + m++; + break; + } + else + { + /* Work around a bug with older shells that "see" a '\' + that is really the 2nd byte of a multibyte character. + In practice the problem is limited to ASCII + chars >= '@' that are shell special chars. */ + if ('[' == 0x5b && elide_outer_quotes + && quoting_style == shell_always_quoting_style) + { + size_t j; + for (j = 1; j < bytes; j++) + switch (arg[i + m + j]) + { + case '[': case '\\': case '^': + case '`': case '|': + goto force_outer_quoting_style; + + default: + break; + } + } + + if (! iswprint (w)) + printable = false; + m += bytes; + } + } + while (! mbsinit (&mbstate)); + } + + if (1 < m || (backslash_escapes && ! printable)) + { + /* Output a multibyte sequence, or an escaped + unprintable unibyte character. */ + size_t ilim = i + m; + + for (;;) + { + if (backslash_escapes && ! printable) + { + if (elide_outer_quotes) + goto force_outer_quoting_style; + STORE ('\\'); + STORE ('0' + (c >> 6)); + STORE ('0' + ((c >> 3) & 7)); + c = '0' + (c & 7); + } + else if (is_right_quote) + { + STORE ('\\'); + is_right_quote = false; + } + if (ilim <= i + 1) + break; + STORE (c); + c = arg[++i]; + } + + goto store_c; + } + } + } + + if (! ((backslash_escapes || elide_outer_quotes) + && quote_these_too + && quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))) + && !is_right_quote) + goto store_c; + + store_escape: + if (elide_outer_quotes) + goto force_outer_quoting_style; + STORE ('\\'); + + store_c: + STORE (c); + } + + if (len == 0 && quoting_style == shell_always_quoting_style + && elide_outer_quotes) + goto force_outer_quoting_style; + + if (quote_string && !elide_outer_quotes) + for (; *quote_string; quote_string++) + STORE (*quote_string); + + if (len < buffersize) + buffer[len] = '\0'; + return len; + + force_outer_quoting_style: + /* Don't reuse quote_these_too, since the addition of outer quotes + sufficiently quotes the specified characters. */ + return quotearg_buffer_restyled (buffer, buffersize, arg, argsize, + quoting_style, + flags & ~QA_ELIDE_OUTER_QUOTES, NULL, + left_quote, right_quote); +} + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using O to control quoting. + If O is null, use the default. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is SIZE_MAX, use the string length of the argument for + ARGSIZE. */ +size_t +quotearg_buffer (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + struct quoting_options const *o) +{ + struct quoting_options const *p = o ? o : &default_quoting_options; + int e = errno; + size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize, + p->style, p->flags, p->quote_these_too, + p->left_quote, p->right_quote); + errno = e; + return r; +} + +/* Equivalent to quotearg_alloc (ARG, ARGSIZE, NULL, O). */ +char * +quotearg_alloc (char const *arg, size_t argsize, + struct quoting_options const *o) +{ + return quotearg_alloc_mem (arg, argsize, NULL, o); +} + +/* Like quotearg_buffer (..., ARG, ARGSIZE, O), except return newly + allocated storage containing the quoted string, and store the + resulting size into *SIZE, if non-NULL. The result can contain + embedded null bytes only if ARGSIZE is not SIZE_MAX, SIZE is not + NULL, and set_quoting_flags has not set the null byte elision + flag. */ +char * +quotearg_alloc_mem (char const *arg, size_t argsize, size_t *size, + struct quoting_options const *o) +{ + struct quoting_options const *p = o ? o : &default_quoting_options; + int e = errno; + /* Elide embedded null bytes if we can't return a size. */ + int flags = p->flags | (size ? 0 : QA_ELIDE_NULL_BYTES); + size_t bufsize = quotearg_buffer_restyled (0, 0, arg, argsize, p->style, + flags, p->quote_these_too, + p->left_quote, + p->right_quote) + 1; + char *buf = xcharalloc (bufsize); + quotearg_buffer_restyled (buf, bufsize, arg, argsize, p->style, flags, + p->quote_these_too, + p->left_quote, p->right_quote); + errno = e; + if (size) + *size = bufsize - 1; + return buf; +} + +/* A storage slot with size and pointer to a value. */ +struct slotvec +{ + size_t size; + char *val; +}; + +/* Preallocate a slot 0 buffer, so that the caller can always quote + one small component of a "memory exhausted" message in slot 0. */ +static char slot0[256]; +static unsigned int nslots = 1; +static struct slotvec slotvec0 = {sizeof slot0, slot0}; +static struct slotvec *slotvec = &slotvec0; + +void +quotearg_free (void) +{ + struct slotvec *sv = slotvec; + unsigned int i; + for (i = 1; i < nslots; i++) + free (sv[i].val); + if (sv[0].val != slot0) + { + free (sv[0].val); + slotvec0.size = sizeof slot0; + slotvec0.val = slot0; + } + if (sv != &slotvec0) + { + free (sv); + slotvec = &slotvec0; + } + nslots = 1; +} + +/* Use storage slot N to return a quoted version of argument ARG. + ARG is of size ARGSIZE, but if that is SIZE_MAX, ARG is a + null-terminated string. + OPTIONS specifies the quoting options. + The returned value points to static storage that can be + reused by the next call to this function with the same value of N. + N must be nonnegative. N is deliberately declared with type "int" + to allow for future extensions (using negative values). */ +static char * +quotearg_n_options (int n, char const *arg, size_t argsize, + struct quoting_options const *options) +{ + int e = errno; + + unsigned int n0 = n; + struct slotvec *sv = slotvec; + + if (n < 0) + abort (); + + if (nslots <= n0) + { + /* FIXME: technically, the type of n1 should be `unsigned int', + but that evokes an unsuppressible warning from gcc-4.0.1 and + older. If gcc ever provides an option to suppress that warning, + revert to the original type, so that the test in xalloc_oversized + is once again performed only at compile time. */ + size_t n1 = n0 + 1; + bool preallocated = (sv == &slotvec0); + + if (xalloc_oversized (n1, sizeof *sv)) + xalloc_die (); + + slotvec = sv = xrealloc (preallocated ? NULL : sv, n1 * sizeof *sv); + if (preallocated) + *sv = slotvec0; + memset (sv + nslots, 0, (n1 - nslots) * sizeof *sv); + nslots = n1; + } + + { + size_t size = sv[n].size; + char *val = sv[n].val; + /* Elide embedded null bytes since we don't return a size. */ + int flags = options->flags | QA_ELIDE_NULL_BYTES; + size_t qsize = quotearg_buffer_restyled (val, size, arg, argsize, + options->style, flags, + options->quote_these_too, + options->left_quote, + options->right_quote); + + if (size <= qsize) + { + sv[n].size = size = qsize + 1; + if (val != slot0) + free (val); + sv[n].val = val = xcharalloc (size); + quotearg_buffer_restyled (val, size, arg, argsize, options->style, + flags, options->quote_these_too, + options->left_quote, + options->right_quote); + } + + errno = e; + return val; + } +} + +char * +quotearg_n (int n, char const *arg) +{ + return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options); +} + +char * +quotearg_n_mem (int n, char const *arg, size_t argsize) +{ + return quotearg_n_options (n, arg, argsize, &default_quoting_options); +} + +char * +quotearg (char const *arg) +{ + return quotearg_n (0, arg); +} + +char * +quotearg_mem (char const *arg, size_t argsize) +{ + return quotearg_n_mem (0, arg, argsize); +} + +char * +quotearg_n_style (int n, enum quoting_style s, char const *arg) +{ + struct quoting_options const o = quoting_options_from_style (s); + return quotearg_n_options (n, arg, SIZE_MAX, &o); +} + +char * +quotearg_n_style_mem (int n, enum quoting_style s, + char const *arg, size_t argsize) +{ + struct quoting_options const o = quoting_options_from_style (s); + return quotearg_n_options (n, arg, argsize, &o); +} + +char * +quotearg_style (enum quoting_style s, char const *arg) +{ + return quotearg_n_style (0, s, arg); +} + +char * +quotearg_style_mem (enum quoting_style s, char const *arg, size_t argsize) +{ + return quotearg_n_style_mem (0, s, arg, argsize); +} + +char * +quotearg_char_mem (char const *arg, size_t argsize, char ch) +{ + struct quoting_options options; + options = default_quoting_options; + set_char_quoting (&options, ch, 1); + return quotearg_n_options (0, arg, argsize, &options); +} + +char * +quotearg_char (char const *arg, char ch) +{ + return quotearg_char_mem (arg, SIZE_MAX, ch); +} + +char * +quotearg_colon (char const *arg) +{ + return quotearg_char (arg, ':'); +} + +char * +quotearg_colon_mem (char const *arg, size_t argsize) +{ + return quotearg_char_mem (arg, argsize, ':'); +} + +char * +quotearg_n_custom (int n, char const *left_quote, + char const *right_quote, char const *arg) +{ + return quotearg_n_custom_mem (n, left_quote, right_quote, arg, + SIZE_MAX); +} + +char * +quotearg_n_custom_mem (int n, char const *left_quote, + char const *right_quote, + char const *arg, size_t argsize) +{ + struct quoting_options o = default_quoting_options; + set_custom_quoting (&o, left_quote, right_quote); + return quotearg_n_options (n, arg, argsize, &o); +} + +char * +quotearg_custom (char const *left_quote, char const *right_quote, + char const *arg) +{ + return quotearg_n_custom (0, left_quote, right_quote, arg); +} + +char * +quotearg_custom_mem (char const *left_quote, char const *right_quote, + char const *arg, size_t argsize) +{ + return quotearg_n_custom_mem (0, left_quote, right_quote, arg, + argsize); +} diff --git a/gnulib/quotearg.h b/gnulib/quotearg.h new file mode 100644 index 0000000..2756d76 --- /dev/null +++ b/gnulib/quotearg.h @@ -0,0 +1,389 @@ +/* quotearg.h - quote arguments for output + + Copyright (C) 1998-2002, 2004, 2006, 2008-2011 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert <eggert@twinsun.com> */ + +#ifndef QUOTEARG_H_ +# define QUOTEARG_H_ 1 + +# include <stddef.h> + +/* Basic quoting styles. For each style, an example is given on the + input strings "simple", "\0 \t\n'\"\033?""?/\\", and "a:b", using + quotearg_buffer, quotearg_mem, and quotearg_colon_mem with that + style and the default flags and quoted characters. Note that the + examples are shown here as valid C strings rather than what + displays on a terminal (with "??/" as a trigraph for "\\"). */ +enum quoting_style + { + /* Output names as-is (ls --quoting-style=literal). Can result in + embedded null bytes if QA_ELIDE_NULL_BYTES is not in + effect. + + quotearg_buffer: + "simple", "\0 \t\n'\"\033??/\\", "a:b" + quotearg: + "simple", " \t\n'\"\033??/\\", "a:b" + quotearg_colon: + "simple", " \t\n'\"\033??/\\", "a:b" + */ + literal_quoting_style, + + /* Quote names for the shell if they contain shell metacharacters + or would cause ambiguous output (ls --quoting-style=shell). + Can result in embedded null bytes if QA_ELIDE_NULL_BYTES is not + in effect. + + quotearg_buffer: + "simple", "'\0 \t\n'\\''\"\033??/\\'", "a:b" + quotearg: + "simple", "' \t\n'\\''\"\033??/\\'", "a:b" + quotearg_colon: + "simple", "' \t\n'\\''\"\033??/\\'", "'a:b'" + */ + shell_quoting_style, + + /* Quote names for the shell, even if they would normally not + require quoting (ls --quoting-style=shell-always). Can result + in embedded null bytes if QA_ELIDE_NULL_BYTES is not in effect. + Behaves like shell_quoting_style if QA_ELIDE_OUTER_QUOTES is in + effect. + + quotearg_buffer: + "'simple'", "'\0 \t\n'\\''\"\033??/\\'", "'a:b'" + quotearg: + "'simple'", "' \t\n'\\''\"\033??/\\'", "'a:b'" + quotearg_colon: + "'simple'", "' \t\n'\\''\"\033??/\\'", "'a:b'" + */ + shell_always_quoting_style, + + /* Quote names as for a C language string (ls --quoting-style=c). + Behaves like c_maybe_quoting_style if QA_ELIDE_OUTER_QUOTES is + in effect. Split into consecutive strings if + QA_SPLIT_TRIGRAPHS. + + quotearg_buffer: + "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\"" + quotearg: + "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\"" + quotearg_colon: + "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a\\:b\"" + */ + c_quoting_style, + + /* Like c_quoting_style except omit the surrounding double-quote + characters if no quoted characters are encountered. + + quotearg_buffer: + "simple", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "a:b" + quotearg: + "simple", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "a:b" + quotearg_colon: + "simple", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\"" + */ + c_maybe_quoting_style, + + /* Like c_quoting_style except always omit the surrounding + double-quote characters and ignore QA_SPLIT_TRIGRAPHS + (ls --quoting-style=escape). + + quotearg_buffer: + "simple", "\\0 \\t\\n'\"\\033??/\\\\", "a:b" + quotearg: + "simple", "\\0 \\t\\n'\"\\033??/\\\\", "a:b" + quotearg_colon: + "simple", "\\0 \\t\\n'\"\\033??/\\\\", "a\\:b" + */ + escape_quoting_style, + + /* Like clocale_quoting_style, but quote `like this' instead of + "like this" in the default C locale (ls --quoting-style=locale). + + LC_MESSAGES=C + quotearg_buffer: + "`simple'", "`\\0 \\t\\n\\'\"\\033??/\\\\'", "`a:b'" + quotearg: + "`simple'", "`\\0 \\t\\n\\'\"\\033??/\\\\'", "`a:b'" + quotearg_colon: + "`simple'", "`\\0 \\t\\n\\'\"\\033??/\\\\'", "`a\\:b'" + + LC_MESSAGES=pt_PT.utf8 + quotearg_buffer: + "\302\253simple\302\273", + "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273" + quotearg: + "\302\253simple\302\273", + "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273" + quotearg_colon: + "\302\253simple\302\273", + "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a\\:b\302\273" + */ + locale_quoting_style, + + /* Like c_quoting_style except use quotation marks appropriate for + the locale and ignore QA_SPLIT_TRIGRAPHS + (ls --quoting-style=clocale). + + LC_MESSAGES=C + quotearg_buffer: + "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\"" + quotearg: + "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a:b\"" + quotearg_colon: + "\"simple\"", "\"\\0 \\t\\n'\\\"\\033??/\\\\\"", "\"a\\:b\"" + + LC_MESSAGES=pt_PT.utf8 + quotearg_buffer: + "\302\253simple\302\273", + "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273" + quotearg: + "\302\253simple\302\273", + "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a:b\302\273" + quotearg_colon: + "\302\253simple\302\273", + "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a\\:b\302\273" + */ + clocale_quoting_style, + + /* Like clocale_quoting_style except use the custom quotation marks + set by set_custom_quoting. If custom quotation marks are not + set, the behavior is undefined. + + left_quote = right_quote = "'" + quotearg_buffer: + "'simple'", "'\\0 \\t\\n\\'\"\\033??/\\\\'", "'a:b'" + quotearg: + "'simple'", "'\\0 \\t\\n\\'\"\\033??/\\\\'", "'a:b'" + quotearg_colon: + "'simple'", "'\\0 \\t\\n\\'\"\\033??/\\\\'", "'a\\:b'" + + left_quote = "(" and right_quote = ")" + quotearg_buffer: + "(simple)", "(\\0 \\t\\n'\"\\033??/\\\\)", "(a:b)" + quotearg: + "(simple)", "(\\0 \\t\\n'\"\\033??/\\\\)", "(a:b)" + quotearg_colon: + "(simple)", "(\\0 \\t\\n'\"\\033??/\\\\)", "(a\\:b)" + + left_quote = ":" and right_quote = " " + quotearg_buffer: + ":simple ", ":\\0\\ \\t\\n'\"\\033??/\\\\ ", ":a:b " + quotearg: + ":simple ", ":\\0\\ \\t\\n'\"\\033??/\\\\ ", ":a:b " + quotearg_colon: + ":simple ", ":\\0\\ \\t\\n'\"\\033??/\\\\ ", ":a\\:b " + + left_quote = "\"'" and right_quote = "'\"" + Notice that this is treated as a single level of quotes or two + levels where the outer quote need not be escaped within the inner + quotes. For two levels where the outer quote must be escaped + within the inner quotes, you must use separate quotearg + invocations. + quotearg_buffer: + "\"'simple'\"", "\"'\\0 \\t\\n\\'\"\\033??/\\\\'\"", "\"'a:b'\"" + quotearg: + "\"'simple'\"", "\"'\\0 \\t\\n\\'\"\\033??/\\\\'\"", "\"'a:b'\"" + quotearg_colon: + "\"'simple'\"", "\"'\\0 \\t\\n\\'\"\\033??/\\\\'\"", "\"'a\\:b'\"" + */ + custom_quoting_style + }; + +/* Flags for use in set_quoting_flags. */ +enum quoting_flags + { + /* Always elide null bytes from styles that do not quote them, + even when the length of the result is available to the + caller. */ + QA_ELIDE_NULL_BYTES = 0x01, + + /* Omit the surrounding quote characters if no escaped characters + are encountered. Note that if no other character needs + escaping, then neither does the escape character. */ + QA_ELIDE_OUTER_QUOTES = 0x02, + + /* In the c_quoting_style and c_maybe_quoting_style, split ANSI + trigraph sequences into concatenated strings (for example, + "?""?/" rather than "??/", which could be confused with + "\\"). */ + QA_SPLIT_TRIGRAPHS = 0x04 + }; + +/* For now, --quoting-style=literal is the default, but this may change. */ +# ifndef DEFAULT_QUOTING_STYLE +# define DEFAULT_QUOTING_STYLE literal_quoting_style +# endif + +/* Names of quoting styles and their corresponding values. */ +extern char const *const quoting_style_args[]; +extern enum quoting_style const quoting_style_vals[]; + +struct quoting_options; + +/* The functions listed below set and use a hidden variable + that contains the default quoting style options. */ + +/* Allocate a new set of quoting options, with contents initially identical + to O if O is not null, or to the default if O is null. + It is the caller's responsibility to free the result. */ +struct quoting_options *clone_quoting_options (struct quoting_options *o); + +/* Get the value of O's quoting style. If O is null, use the default. */ +enum quoting_style get_quoting_style (struct quoting_options *o); + +/* In O (or in the default if O is null), + set the value of the quoting style to S. */ +void set_quoting_style (struct quoting_options *o, enum quoting_style s); + +/* In O (or in the default if O is null), + set the value of the quoting options for character C to I. + Return the old value. Currently, the only values defined for I are + 0 (the default) and 1 (which means to quote the character even if + it would not otherwise be quoted). C must never be a digit or a + letter that has special meaning after a backslash (for example, "\t" + for tab). */ +int set_char_quoting (struct quoting_options *o, char c, int i); + +/* In O (or in the default if O is null), + set the value of the quoting options flag to I, which can be a + bitwise combination of enum quoting_flags, or 0 for default + behavior. Return the old value. */ +int set_quoting_flags (struct quoting_options *o, int i); + +/* In O (or in the default if O is null), + set the value of the quoting style to custom_quoting_style, + set the left quote to LEFT_QUOTE, and set the right quote to + RIGHT_QUOTE. Each of LEFT_QUOTE and RIGHT_QUOTE must be + null-terminated and can be the empty string. Because backslashes are + used for escaping, it does not make sense for RIGHT_QUOTE to contain + a backslash. RIGHT_QUOTE must not begin with a digit or a letter + that has special meaning after a backslash (for example, "\t" for + tab). */ +void set_custom_quoting (struct quoting_options *o, + char const *left_quote, + char const *right_quote); + +/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of + argument ARG (of size ARGSIZE), using O to control quoting. + If O is null, use the default. + Terminate the output with a null character, and return the written + size of the output, not counting the terminating null. + If BUFFERSIZE is too small to store the output string, return the + value that would have been returned had BUFFERSIZE been large enough. + If ARGSIZE is -1, use the string length of the argument for ARGSIZE. + On output, BUFFER might contain embedded null bytes if ARGSIZE was + not -1, the style of O does not use backslash escapes, and the + flags of O do not request elision of null bytes.*/ +size_t quotearg_buffer (char *buffer, size_t buffersize, + char const *arg, size_t argsize, + struct quoting_options const *o); + +/* Like quotearg_buffer, except return the result in a newly allocated + buffer. It is the caller's responsibility to free the result. The + result will not contain embedded null bytes. */ +char *quotearg_alloc (char const *arg, size_t argsize, + struct quoting_options const *o); + +/* Like quotearg_alloc, except that the length of the result, + excluding the terminating null byte, is stored into SIZE if it is + non-NULL. The result might contain embedded null bytes if ARGSIZE + was not -1, SIZE was not NULL, the style of O does not use + backslash escapes, and the flags of O do not request elision of + null bytes.*/ +char *quotearg_alloc_mem (char const *arg, size_t argsize, + size_t *size, struct quoting_options const *o); + +/* Use storage slot N to return a quoted version of the string ARG. + Use the default quoting options. + The returned value points to static storage that can be + reused by the next call to this function with the same value of N. + N must be nonnegative. The output of all functions in the + quotearg_n family are guaranteed to not contain embedded null + bytes.*/ +char *quotearg_n (int n, char const *arg); + +/* Equivalent to quotearg_n (0, ARG). */ +char *quotearg (char const *arg); + +/* Use storage slot N to return a quoted version of the argument ARG + of size ARGSIZE. This is like quotearg_n (N, ARG), except it can + quote null bytes. */ +char *quotearg_n_mem (int n, char const *arg, size_t argsize); + +/* Equivalent to quotearg_n_mem (0, ARG, ARGSIZE). */ +char *quotearg_mem (char const *arg, size_t argsize); + +/* Use style S and storage slot N to return a quoted version of the string ARG. + This is like quotearg_n (N, ARG), except that it uses S with no other + options to specify the quoting method. */ +char *quotearg_n_style (int n, enum quoting_style s, char const *arg); + +/* Use style S and storage slot N to return a quoted version of the + argument ARG of size ARGSIZE. This is like quotearg_n_style + (N, S, ARG), except it can quote null bytes. */ +char *quotearg_n_style_mem (int n, enum quoting_style s, + char const *arg, size_t argsize); + +/* Equivalent to quotearg_n_style (0, S, ARG). */ +char *quotearg_style (enum quoting_style s, char const *arg); + +/* Equivalent to quotearg_n_style_mem (0, S, ARG, ARGSIZE). */ +char *quotearg_style_mem (enum quoting_style s, + char const *arg, size_t argsize); + +/* Like quotearg (ARG), except also quote any instances of CH. + See set_char_quoting for a description of acceptable CH values. */ +char *quotearg_char (char const *arg, char ch); + +/* Like quotearg_char (ARG, CH), except it can quote null bytes. */ +char *quotearg_char_mem (char const *arg, size_t argsize, char ch); + +/* Equivalent to quotearg_char (ARG, ':'). */ +char *quotearg_colon (char const *arg); + +/* Like quotearg_colon (ARG), except it can quote null bytes. */ +char *quotearg_colon_mem (char const *arg, size_t argsize); + +/* Like quotearg_n_style (N, S, ARG) but with S as custom_quoting_style + with left quote as LEFT_QUOTE and right quote as RIGHT_QUOTE. See + set_custom_quoting for a description of acceptable LEFT_QUOTE and + RIGHT_QUOTE values. */ +char *quotearg_n_custom (int n, char const *left_quote, + char const *right_quote, char const *arg); + +/* Like quotearg_n_custom (N, LEFT_QUOTE, RIGHT_QUOTE, ARG) except it + can quote null bytes. */ +char *quotearg_n_custom_mem (int n, char const *left_quote, + char const *right_quote, + char const *arg, size_t argsize); + +/* Equivalent to quotearg_n_custom (0, LEFT_QUOTE, RIGHT_QUOTE, ARG). */ +char *quotearg_custom (char const *left_quote, char const *right_quote, + char const *arg); + +/* Equivalent to quotearg_n_custom_mem (0, LEFT_QUOTE, RIGHT_QUOTE, ARG, + ARGSIZE). */ +char *quotearg_custom_mem (char const *left_quote, + char const *right_quote, + char const *arg, size_t argsize); + +/* Free any dynamically allocated memory. */ +void quotearg_free (void); + +#endif /* !QUOTEARG_H_ */ diff --git a/gnulib/version-etc-fsf.c b/gnulib/version-etc-fsf.c new file mode 100644 index 0000000..c821583 --- /dev/null +++ b/gnulib/version-etc-fsf.c @@ -0,0 +1,30 @@ +/* Variable with FSF copyright information, for version-etc. + Copyright (C) 1999-2006, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Jim Meyering. */ + +#include <config.h> + +/* Specification. */ +#include "version-etc.h" + +/* Default copyright goes to the FSF. */ + +const char version_etc_copyright[] = + /* Do *not* mark this string for translation. %s is a copyright + symbol suitable for this locale, and %d is the copyright + year. */ + "Copyright %s %d Free Software Foundation, Inc."; diff --git a/gnulib/version-etc.c b/gnulib/version-etc.c new file mode 100644 index 0000000..b8d4724 --- /dev/null +++ b/gnulib/version-etc.c @@ -0,0 +1,258 @@ +/* Print --version and bug-reporting information in a consistent format. + Copyright (C) 1999-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Jim Meyering. */ + +#include <config.h> + +/* Specification. */ +#include "version-etc.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +/* If you use AM_INIT_AUTOMAKE's no-define option, + PACKAGE is not defined. Use PACKAGE_TARNAME instead. */ +#if ! defined PACKAGE && defined PACKAGE_TARNAME +# define PACKAGE PACKAGE_TARNAME +#endif + +enum { COPYRIGHT_YEAR = 2011 }; + +/* The three functions below display the --version information the + standard way. + + If COMMAND_NAME is NULL, the PACKAGE is assumed to be the name of + the program. The formats are therefore: + + PACKAGE VERSION + + or + + COMMAND_NAME (PACKAGE) VERSION. + + The functions differ in the way they are passed author names. */ + +/* Display the --version information the standard way. + + Author names are given in the array AUTHORS. N_AUTHORS is the + number of elements in the array. */ +void +version_etc_arn (FILE *stream, + const char *command_name, const char *package, + const char *version, + const char * const * authors, size_t n_authors) +{ + if (command_name) + fprintf (stream, "%s (%s) %s\n", command_name, package, version); + else + fprintf (stream, "%s %s\n", package, version); + +#ifdef PACKAGE_PACKAGER +# ifdef PACKAGE_PACKAGER_VERSION + fprintf (stream, _("Packaged by %s (%s)\n"), PACKAGE_PACKAGER, + PACKAGE_PACKAGER_VERSION); +# else + fprintf (stream, _("Packaged by %s\n"), PACKAGE_PACKAGER); +# endif +#endif + + /* TRANSLATORS: Translate "(C)" to the copyright symbol + (C-in-a-circle), if this symbol is available in the user's + locale. Otherwise, do not translate "(C)"; leave it as-is. */ + fprintf (stream, version_etc_copyright, _("(C)"), COPYRIGHT_YEAR); + + fputs (_("\ +\n\ +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\ +This is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law.\n\ +\n\ +"), + stream); + + switch (n_authors) + { + case 0: + /* The caller must provide at least one author name. */ + abort (); + case 1: + /* TRANSLATORS: %s denotes an author name. */ + fprintf (stream, _("Written by %s.\n"), authors[0]); + break; + case 2: + /* TRANSLATORS: Each %s denotes an author name. */ + fprintf (stream, _("Written by %s and %s.\n"), authors[0], authors[1]); + break; + case 3: + /* TRANSLATORS: Each %s denotes an author name. */ + fprintf (stream, _("Written by %s, %s, and %s.\n"), + authors[0], authors[1], authors[2]); + break; + case 4: + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("Written by %s, %s, %s,\nand %s.\n"), + authors[0], authors[1], authors[2], authors[3]); + break; + case 5: + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("Written by %s, %s, %s,\n%s, and %s.\n"), + authors[0], authors[1], authors[2], authors[3], authors[4]); + break; + case 6: + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("Written by %s, %s, %s,\n%s, %s, and %s.\n"), + authors[0], authors[1], authors[2], authors[3], authors[4], + authors[5]); + break; + case 7: + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("Written by %s, %s, %s,\n%s, %s, %s, and %s.\n"), + authors[0], authors[1], authors[2], authors[3], authors[4], + authors[5], authors[6]); + break; + case 8: + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("\ +Written by %s, %s, %s,\n%s, %s, %s, %s,\nand %s.\n"), + authors[0], authors[1], authors[2], authors[3], authors[4], + authors[5], authors[6], authors[7]); + break; + case 9: + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("\ +Written by %s, %s, %s,\n%s, %s, %s, %s,\n%s, and %s.\n"), + authors[0], authors[1], authors[2], authors[3], authors[4], + authors[5], authors[6], authors[7], authors[8]); + break; + default: + /* 10 or more authors. Use an abbreviation, since the human reader + will probably not want to read the entire list anyway. */ + /* TRANSLATORS: Each %s denotes an author name. + You can use line breaks, estimating that each author name occupies + ca. 16 screen columns and that a screen line has ca. 80 columns. */ + fprintf (stream, _("\ +Written by %s, %s, %s,\n%s, %s, %s, %s,\n%s, %s, and others.\n"), + authors[0], authors[1], authors[2], authors[3], authors[4], + authors[5], authors[6], authors[7], authors[8]); + break; + } +} + +/* Display the --version information the standard way. See the initial + comment to this module, for more information. + + Author names are given in the NULL-terminated array AUTHORS. */ +void +version_etc_ar (FILE *stream, + const char *command_name, const char *package, + const char *version, const char * const * authors) +{ + size_t n_authors; + + for (n_authors = 0; authors[n_authors]; n_authors++) + ; + version_etc_arn (stream, command_name, package, version, authors, n_authors); +} + +/* Display the --version information the standard way. See the initial + comment to this module, for more information. + + Author names are given in the NULL-terminated va_list AUTHORS. */ +void +version_etc_va (FILE *stream, + const char *command_name, const char *package, + const char *version, va_list authors) +{ + size_t n_authors; + const char *authtab[10]; + + for (n_authors = 0; + n_authors < 10 + && (authtab[n_authors] = va_arg (authors, const char *)) != NULL; + n_authors++) + ; + version_etc_arn (stream, command_name, package, version, + authtab, n_authors); +} + + +/* Display the --version information the standard way. + + If COMMAND_NAME is NULL, the PACKAGE is assumed to be the name of + the program. The formats are therefore: + + PACKAGE VERSION + + or + + COMMAND_NAME (PACKAGE) VERSION. + + The authors names are passed as separate arguments, with an additional + NULL argument at the end. */ +void +version_etc (FILE *stream, + const char *command_name, const char *package, + const char *version, /* const char *author1, ...*/ ...) +{ + va_list authors; + + va_start (authors, version); + version_etc_va (stream, command_name, package, version, authors); + va_end (authors); +} + +void +emit_bug_reporting_address (void) +{ + /* TRANSLATORS: The placeholder indicates the bug-reporting address + for this package. Please add _another line_ saying + "Report translation bugs to <...>\n" with the address for translation + bugs (typically your translation team's web or email address). */ + printf (_("\nReport bugs to: %s\n"), PACKAGE_BUGREPORT); +#ifdef PACKAGE_PACKAGER_BUG_REPORTS + printf (_("Report %s bugs to: %s\n"), PACKAGE_PACKAGER, + PACKAGE_PACKAGER_BUG_REPORTS); +#endif +#ifdef PACKAGE_URL + printf (_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +#else + printf (_("%s home page: <http://www.gnu.org/software/%s/>\n"), + PACKAGE_NAME, PACKAGE); +#endif + fputs (_("General help using GNU software: <http://www.gnu.org/gethelp/>\n"), + stdout); +} diff --git a/gnulib/xalloc-die.c b/gnulib/xalloc-die.c new file mode 100644 index 0000000..aba4a06 --- /dev/null +++ b/gnulib/xalloc-die.c @@ -0,0 +1,41 @@ +/* Report a memory allocation failure and exit. + + Copyright (C) 1997-2000, 2002-2004, 2006, 2009-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "xalloc.h" + +#include <stdlib.h> + +#include "error.h" +#include "exitfail.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +void +xalloc_die (void) +{ + error (exit_failure, 0, "%s", _("memory exhausted")); + + /* The `noreturn' cannot be given to error, since it may return if + its first argument is 0. To help compilers understand the + xalloc_die does not return, call abort. Also, the abort is a + safety feature if exit_failure is 0 (which shouldn't happen). */ + abort (); +} diff --git a/gnulib/xmalloc.c b/gnulib/xmalloc.c new file mode 100644 index 0000000..08c30fb --- /dev/null +++ b/gnulib/xmalloc.c @@ -0,0 +1,124 @@ +/* xmalloc.c -- malloc with out of memory checking + + Copyright (C) 1990-2000, 2002-2006, 2008-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#if ! HAVE_INLINE +# define static_inline +#endif +#include "xalloc.h" +#undef static_inline + +#include <stdlib.h> +#include <string.h> + +/* 1 if calloc is known to be compatible with GNU calloc. This + matters if we are not also using the calloc module, which defines + HAVE_CALLOC_GNU and supports the GNU API even on non-GNU platforms. */ +#if defined HAVE_CALLOC_GNU || (defined __GLIBC__ && !defined __UCLIBC__) +enum { HAVE_GNU_CALLOC = 1 }; +#else +enum { HAVE_GNU_CALLOC = 0 }; +#endif + +/* Allocate N bytes of memory dynamically, with error checking. */ + +void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p && n != 0) + xalloc_die (); + return p; +} + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. */ + +void * +xrealloc (void *p, size_t n) +{ + if (!n && p) + { + /* The GNU and C99 realloc behaviors disagree here. Act like + GNU, even if the underlying realloc is C99. */ + free (p); + return NULL; + } + + p = realloc (p, n); + if (!p && n) + xalloc_die (); + return p; +} + +/* If P is null, allocate a block of at least *PN bytes; otherwise, + reallocate P so that it contains more than *PN bytes. *PN must be + nonzero unless P is null. Set *PN to the new block's size, and + return the pointer to the new block. *PN is never set to zero, and + the returned pointer is never null. */ + +void * +x2realloc (void *p, size_t *pn) +{ + return x2nrealloc (p, pn, 1); +} + +/* Allocate S bytes of zeroed memory dynamically, with error checking. + There's no need for xnzalloc (N, S), since it would be equivalent + to xcalloc (N, S). */ + +void * +xzalloc (size_t s) +{ + return memset (xmalloc (s), 0, s); +} + +/* Allocate zeroed memory for N elements of S bytes, with error + checking. S must be nonzero. */ + +void * +xcalloc (size_t n, size_t s) +{ + void *p; + /* Test for overflow, since some calloc implementations don't have + proper overflow checks. But omit overflow and size-zero tests if + HAVE_GNU_CALLOC, since GNU calloc catches overflow and never + returns NULL if successful. */ + if ((! HAVE_GNU_CALLOC && xalloc_oversized (n, s)) + || (! (p = calloc (n, s)) && (HAVE_GNU_CALLOC || n != 0))) + xalloc_die (); + return p; +} + +/* Clone an object P of size S, with error checking. There's no need + for xnmemdup (P, N, S), since xmemdup (P, N * S) works without any + need for an arithmetic overflow check. */ + +void * +xmemdup (void const *p, size_t s) +{ + return memcpy (xmalloc (s), p, s); +} + +/* Clone STRING. */ + +char * +xstrdup (char const *string) +{ + return xmemdup (string, strlen (string) + 1); +} diff --git a/gnulib/xstrtol.c b/gnulib/xstrtol.c new file mode 100644 index 0000000..97ebd90 --- /dev/null +++ b/gnulib/xstrtol.c @@ -0,0 +1,228 @@ +/* A more useful interface to strtol. + + Copyright (C) 1995-1996, 1998-2001, 2003-2007, 2009-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Jim Meyering. */ + +#ifndef __strtol +# define __strtol strtol +# define __strtol_t long int +# define __xstrtol xstrtol +# define STRTOL_T_MINIMUM LONG_MIN +# define STRTOL_T_MAXIMUM LONG_MAX +#endif + +#include <config.h> + +#include "xstrtol.h" + +/* Some pre-ANSI implementations (e.g. SunOS 4) + need stderr defined if assertion checking is enabled. */ +#include <stdio.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "intprops.h" + +static strtol_error +bkm_scale (__strtol_t *x, int scale_factor) +{ + if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor) + { + *x = STRTOL_T_MINIMUM; + return LONGINT_OVERFLOW; + } + if (STRTOL_T_MAXIMUM / scale_factor < *x) + { + *x = STRTOL_T_MAXIMUM; + return LONGINT_OVERFLOW; + } + *x *= scale_factor; + return LONGINT_OK; +} + +static strtol_error +bkm_scale_by_power (__strtol_t *x, int base, int power) +{ + strtol_error err = LONGINT_OK; + while (power--) + err |= bkm_scale (x, base); + return err; +} + +/* FIXME: comment. */ + +strtol_error +__xstrtol (const char *s, char **ptr, int strtol_base, + __strtol_t *val, const char *valid_suffixes) +{ + char *t_ptr; + char **p; + __strtol_t tmp; + strtol_error err = LONGINT_OK; + + assert (0 <= strtol_base && strtol_base <= 36); + + p = (ptr ? ptr : &t_ptr); + + if (! TYPE_SIGNED (__strtol_t)) + { + const char *q = s; + unsigned char ch = *q; + while (isspace (ch)) + ch = *++q; + if (ch == '-') + return LONGINT_INVALID; + } + + errno = 0; + tmp = __strtol (s, p, strtol_base); + + if (*p == s) + { + /* If there is no number but there is a valid suffix, assume the + number is 1. The string is invalid otherwise. */ + if (valid_suffixes && **p && strchr (valid_suffixes, **p)) + tmp = 1; + else + return LONGINT_INVALID; + } + else if (errno != 0) + { + if (errno != ERANGE) + return LONGINT_INVALID; + err = LONGINT_OVERFLOW; + } + + /* Let valid_suffixes == NULL mean `allow any suffix'. */ + /* FIXME: update all callers except the ones that allow suffixes + after the number, changing last parameter NULL to `""'. */ + if (!valid_suffixes) + { + *val = tmp; + return err; + } + + if (**p != '\0') + { + int base = 1024; + int suffixes = 1; + strtol_error overflow; + + if (!strchr (valid_suffixes, **p)) + { + *val = tmp; + return err | LONGINT_INVALID_SUFFIX_CHAR; + } + + if (strchr (valid_suffixes, '0')) + { + /* The ``valid suffix'' '0' is a special flag meaning that + an optional second suffix is allowed, which can change + the base. A suffix "B" (e.g. "100MB") stands for a power + of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for + a power of 1024. If no suffix (e.g. "100M"), assume + power-of-1024. */ + + switch (p[0][1]) + { + case 'i': + if (p[0][2] == 'B') + suffixes += 2; + break; + + case 'B': + case 'D': /* 'D' is obsolescent */ + base = 1000; + suffixes++; + break; + } + } + + switch (**p) + { + case 'b': + overflow = bkm_scale (&tmp, 512); + break; + + case 'B': + overflow = bkm_scale (&tmp, 1024); + break; + + case 'c': + overflow = 0; + break; + + case 'E': /* exa or exbi */ + overflow = bkm_scale_by_power (&tmp, base, 6); + break; + + case 'G': /* giga or gibi */ + case 'g': /* 'g' is undocumented; for compatibility only */ + overflow = bkm_scale_by_power (&tmp, base, 3); + break; + + case 'k': /* kilo */ + case 'K': /* kibi */ + overflow = bkm_scale_by_power (&tmp, base, 1); + break; + + case 'M': /* mega or mebi */ + case 'm': /* 'm' is undocumented; for compatibility only */ + overflow = bkm_scale_by_power (&tmp, base, 2); + break; + + case 'P': /* peta or pebi */ + overflow = bkm_scale_by_power (&tmp, base, 5); + break; + + case 'T': /* tera or tebi */ + case 't': /* 't' is undocumented; for compatibility only */ + overflow = bkm_scale_by_power (&tmp, base, 4); + break; + + case 'w': + overflow = bkm_scale (&tmp, 2); + break; + + case 'Y': /* yotta or 2**80 */ + overflow = bkm_scale_by_power (&tmp, base, 8); + break; + + case 'Z': /* zetta or 2**70 */ + overflow = bkm_scale_by_power (&tmp, base, 7); + break; + + default: + *val = tmp; + return err | LONGINT_INVALID_SUFFIX_CHAR; + } + + err |= overflow; + *p += suffixes; + if (**p) + err |= LONGINT_INVALID_SUFFIX_CHAR; + } + + *val = tmp; + return err; +} diff --git a/gnulib/xstrtoll.c b/gnulib/xstrtoll.c new file mode 100644 index 0000000..db26e87 --- /dev/null +++ b/gnulib/xstrtoll.c @@ -0,0 +1,6 @@ +#define __strtol strtoll +#define __strtol_t long long int +#define __xstrtol xstrtoll +#define STRTOL_T_MINIMUM LLONG_MIN +#define STRTOL_T_MAXIMUM LLONG_MAX +#include "xstrtol.c" diff --git a/include/argmatch.h b/include/argmatch.h new file mode 100644 index 0000000..f87729d --- /dev/null +++ b/include/argmatch.h @@ -0,0 +1,102 @@ +/* argmatch.h -- definitions and prototypes for argmatch.c + + Copyright (C) 1990, 1998-1999, 2001-2002, 2004-2005, 2009-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by David MacKenzie <djm@ai.mit.edu> + Modified by Akim Demaille <demaille@inf.enst.fr> */ + +#ifndef ARGMATCH_H_ +# define ARGMATCH_H_ 1 + +# include <stddef.h> + +# include "verify.h" + +# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array)) + +/* Assert there are as many real arguments as there are values + (argument list ends with a NULL guard). */ + +# define ARGMATCH_VERIFY(Arglist, Vallist) \ + verify (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1) + +/* Return the index of the element of ARGLIST (NULL terminated) that + matches with ARG. If VALLIST is not NULL, then use it to resolve + false ambiguities (i.e., different matches of ARG but corresponding + to the same values in VALLIST). */ + +ptrdiff_t argmatch (char const *arg, char const *const *arglist, + char const *vallist, size_t valsize); + +# define ARGMATCH(Arg, Arglist, Vallist) \ + argmatch (Arg, Arglist, (char const *) (Vallist), sizeof *(Vallist)) + +/* xargmatch calls this function when it fails. This function should not + return. By default, this is a function that calls ARGMATCH_DIE which + in turn defaults to `exit (exit_failure)'. */ +typedef void (*argmatch_exit_fn) (void); +extern argmatch_exit_fn argmatch_die; + +/* Report on stderr why argmatch failed. Report correct values. */ + +void argmatch_invalid (char const *context, char const *value, + ptrdiff_t problem); + +/* Left for compatibility with the old name invalid_arg */ + +# define invalid_arg(Context, Value, Problem) \ + argmatch_invalid (Context, Value, Problem) + + + +/* Report on stderr the list of possible arguments. */ + +void argmatch_valid (char const *const *arglist, + char const *vallist, size_t valsize); + +# define ARGMATCH_VALID(Arglist, Vallist) \ + argmatch_valid (Arglist, (char const *) (Vallist), sizeof *(Vallist)) + + + +/* Same as argmatch, but upon failure, report an explanation of the + failure, and exit using the function EXIT_FN. */ + +ptrdiff_t __xargmatch_internal (char const *context, + char const *arg, char const *const *arglist, + char const *vallist, size_t valsize, + argmatch_exit_fn exit_fn); + +/* Programmer friendly interface to __xargmatch_internal. */ + +# define XARGMATCH(Context, Arg, Arglist, Vallist) \ + ((Vallist) [__xargmatch_internal (Context, Arg, Arglist, \ + (char const *) (Vallist), \ + sizeof *(Vallist), \ + argmatch_die)]) + +/* Convert a value into a corresponding argument. */ + +char const *argmatch_to_argument (char const *value, + char const *const *arglist, + char const *vallist, size_t valsize); + +# define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \ + argmatch_to_argument (Value, Arglist, \ + (char const *) (Vallist), sizeof *(Vallist)) + +#endif /* ARGMATCH_H_ */ diff --git a/include/closeout.h b/include/closeout.h new file mode 100644 index 0000000..ec8d7a6 --- /dev/null +++ b/include/closeout.h @@ -0,0 +1,36 @@ +/* Close standard output and standard error. + + Copyright (C) 1998, 2000, 2003-2004, 2006, 2008-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef CLOSEOUT_H +# define CLOSEOUT_H 1 + +# include <stdbool.h> + +# ifdef __cplusplus +extern "C" { +# endif + +void close_stdout_set_file_name (const char *file); +void close_stdout_set_ignore_EPIPE (bool ignore); +void close_stdout (void); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..37e0c83 --- /dev/null +++ b/include/config.h @@ -0,0 +1,1572 @@ +/* lib/config.h. Generated from config.h.in by configure. */ +/* lib/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to the number of bits in type 'ptrdiff_t'. */ +/* #undef BITSIZEOF_PTRDIFF_T */ + +/* Define to the number of bits in type 'sig_atomic_t'. */ +/* #undef BITSIZEOF_SIG_ATOMIC_T */ + +/* Define to the number of bits in type 'size_t'. */ +/* #undef BITSIZEOF_SIZE_T */ + +/* Define to the number of bits in type 'wchar_t'. */ +/* #undef BITSIZEOF_WCHAR_T */ + +/* Define to the number of bits in type 'wint_t'. */ +/* #undef BITSIZEOF_WINT_T */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Enable assertions, etc. */ +#define DEBUG 1 + +/* Probing functionality only */ +/* #undef DISCOVER_ONLY */ + +/* Define to 1 if // is a file system root distinct from /. */ +/* #undef DOUBLE_SLASH_IS_DISTINCT_ROOT */ + +/* Lazy linking to fs libs */ +#define DYNAMIC_LOADING 1 + +/* device mapper (libdevmapper) support */ +#define ENABLE_DEVICE_MAPPER 1 + +/* Include file system support. i.e. libparted/fs_... */ +#define ENABLE_FS 1 + +/* Mtrace malloc() debugging */ +/* #undef ENABLE_MTRACE */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#define ENABLE_NLS 1 + +/* Include PC98 partition tables. (Sometimes excluded to avoid collisions with + msdos partition tables */ +#define ENABLE_PC98 1 + +/* Define to 1 if the system's ftello function has the Solaris bug. */ +/* #undef FTELLO_BROKEN_AFTER_SWITCHING_FROM_READ_TO_WRITE */ + +/* Define to 1 if nl_langinfo (YESEXPR) returns a non-empty string. */ +#define FUNC_NL_LANGINFO_YESEXPR_WORKS 1 + +/* Define to 1 if realpath() can malloc memory, always gives an absolute path, + and handles trailing slash correctly. */ +#define FUNC_REALPATH_WORKS 1 + +/* Define to 1 if ungetc is broken when used on arbitrary bytes. */ +/* #undef FUNC_UNGETC_BROKEN */ + +/* Define if gettimeofday clobbers the localtime buffer. */ +/* #undef GETTIMEOFDAY_CLOBBERS_LOCALTIME */ + +/* Define this to 'void' or 'struct timezone' to match the system's + declaration of the second argument to gettimeofday. */ +#define GETTIMEOFDAY_TIMEZONE struct timezone + +/* Define to make the limit macros in <stdint.h> visible. */ +#define GL_TRIGGER_STDC_LIMIT_MACROS 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module calloc-gnu shall be considered present. */ +#define GNULIB_CALLOC_GNU 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module canonicalize-lgpl shall be considered present. */ +#define GNULIB_CANONICALIZE_LGPL 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module close-stream shall be considered present. */ +#define GNULIB_CLOSE_STREAM 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module dirname shall be considered present. */ +#define GNULIB_DIRNAME 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module fflush shall be considered present. */ +#define GNULIB_FFLUSH 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module malloc-gnu shall be considered present. */ +#define GNULIB_MALLOC_GNU 1 + +/* enable some gnulib portability checks */ +/* #undef GNULIB_PORTCHECK */ + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module realloc-gnu shall be considered present. */ +#define GNULIB_REALLOC_GNU 1 + +/* Define to a C preprocessor expression that evaluates to 1 or 0, depending + whether the gnulib module strerror shall be considered present. */ +#define GNULIB_STRERROR 1 + +/* Define to 1 when the gnulib module btowc should be tested. */ +#define GNULIB_TEST_BTOWC 1 + +/* Define to 1 when the gnulib module calloc-posix should be tested. */ +#define GNULIB_TEST_CALLOC_POSIX 1 + +/* Define to 1 when the gnulib module canonicalize_file_name should be tested. + */ +#define GNULIB_TEST_CANONICALIZE_FILE_NAME 1 + +/* Define to 1 when the gnulib module close should be tested. */ +#define GNULIB_TEST_CLOSE 1 + +/* Define to 1 when the gnulib module dup2 should be tested. */ +#define GNULIB_TEST_DUP2 1 + +/* Define to 1 when the gnulib module environ should be tested. */ +#define GNULIB_TEST_ENVIRON 1 + +/* Define to 1 when the gnulib module fclose should be tested. */ +#define GNULIB_TEST_FCLOSE 1 + +/* Define to 1 when the gnulib module fflush should be tested. */ +#define GNULIB_TEST_FFLUSH 1 + +/* Define to 1 when the gnulib module fpurge should be tested. */ +#define GNULIB_TEST_FPURGE 1 + +/* Define to 1 when the gnulib module fseeko should be tested. */ +#define GNULIB_TEST_FSEEKO 1 + +/* Define to 1 when the gnulib module fsync should be tested. */ +#define GNULIB_TEST_FSYNC 1 + +/* Define to 1 when the gnulib module ftell should be tested. */ +#define GNULIB_TEST_FTELL 1 + +/* Define to 1 when the gnulib module ftello should be tested. */ +#define GNULIB_TEST_FTELLO 1 + +/* Define to 1 when the gnulib module getopt-gnu should be tested. */ +#define GNULIB_TEST_GETOPT_GNU 1 + +/* Define to 1 when the gnulib module getpagesize should be tested. */ +#define GNULIB_TEST_GETPAGESIZE 1 + +/* Define to 1 when the gnulib module gettimeofday should be tested. */ +#define GNULIB_TEST_GETTIMEOFDAY 1 + +/* Define to 1 when the gnulib module lseek should be tested. */ +#define GNULIB_TEST_LSEEK 1 + +/* Define to 1 when the gnulib module lstat should be tested. */ +#define GNULIB_TEST_LSTAT 1 + +/* Define to 1 when the gnulib module malloc-posix should be tested. */ +#define GNULIB_TEST_MALLOC_POSIX 1 + +/* Define to 1 when the gnulib module mbrtowc should be tested. */ +#define GNULIB_TEST_MBRTOWC 1 + +/* Define to 1 when the gnulib module mbsinit should be tested. */ +#define GNULIB_TEST_MBSINIT 1 + +/* Define to 1 when the gnulib module mbtowc should be tested. */ +#define GNULIB_TEST_MBTOWC 1 + +/* Define to 1 when the gnulib module mkstemp should be tested. */ +#define GNULIB_TEST_MKSTEMP 1 + +/* Define to 1 when the gnulib module nl_langinfo should be tested. */ +#define GNULIB_TEST_NL_LANGINFO 1 + +/* Define to 1 when the gnulib module open should be tested. */ +#define GNULIB_TEST_OPEN 1 + +/* Define to 1 when the gnulib module putenv should be tested. */ +#define GNULIB_TEST_PUTENV 1 + +/* Define to 1 when the gnulib module read should be tested. */ +#define GNULIB_TEST_READ 1 + +/* Define to 1 when the gnulib module readlink should be tested. */ +#define GNULIB_TEST_READLINK 1 + +/* Define to 1 when the gnulib module realloc-posix should be tested. */ +#define GNULIB_TEST_REALLOC_POSIX 1 + +/* Define to 1 when the gnulib module realpath should be tested. */ +#define GNULIB_TEST_REALPATH 1 + +/* Define to 1 when the gnulib module rpmatch should be tested. */ +#define GNULIB_TEST_RPMATCH 1 + +/* Define to 1 when the gnulib module setenv should be tested. */ +#define GNULIB_TEST_SETENV 1 + +/* Define to 1 when the gnulib module setlocale should be tested. */ +#define GNULIB_TEST_SETLOCALE 1 + +/* Define to 1 when the gnulib module sleep should be tested. */ +#define GNULIB_TEST_SLEEP 1 + +/* Define to 1 when the gnulib module stat should be tested. */ +#define GNULIB_TEST_STAT 1 + +/* Define to 1 when the gnulib module strdup should be tested. */ +#define GNULIB_TEST_STRDUP 1 + +/* Define to 1 when the gnulib module strerror should be tested. */ +#define GNULIB_TEST_STRERROR 1 + +/* Define to 1 when the gnulib module strerror_r should be tested. */ +#define GNULIB_TEST_STRERROR_R 1 + +/* Define to 1 when the gnulib module strndup should be tested. */ +#define GNULIB_TEST_STRNDUP 1 + +/* Define to 1 when the gnulib module strnlen should be tested. */ +#define GNULIB_TEST_STRNLEN 1 + +/* Define to 1 when the gnulib module strtoll should be tested. */ +#define GNULIB_TEST_STRTOLL 1 + +/* Define to 1 when the gnulib module strtoull should be tested. */ +#define GNULIB_TEST_STRTOULL 1 + +/* Define to 1 when the gnulib module symlink should be tested. */ +#define GNULIB_TEST_SYMLINK 1 + +/* Define to 1 when the gnulib module unlink should be tested. */ +#define GNULIB_TEST_UNLINK 1 + +/* Define to 1 when the gnulib module unsetenv should be tested. */ +#define GNULIB_TEST_UNSETENV 1 + +/* Define to 1 when the gnulib module usleep should be tested. */ +#define GNULIB_TEST_USLEEP 1 + +/* Define to 1 when the gnulib module wcrtomb should be tested. */ +#define GNULIB_TEST_WCRTOMB 1 + +/* Define to 1 when the gnulib module wctob should be tested. */ +#define GNULIB_TEST_WCTOB 1 + +/* Define to 1 when the gnulib module wctomb should be tested. */ +#define GNULIB_TEST_WCTOMB 1 + +/* Define to 1 if you have 'alloca' after including <alloca.h>, a header that + may be supplied by this distribution. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Has backtrace support */ +#define HAVE_BACKTRACE 1 + +/* Define to 1 if you have the <blkid/blkid.h> header file. */ +/* #undef HAVE_BLKID_BLKID_H */ + +/* Define to 1 if you have the `btowc' function. */ +#define HAVE_BTOWC 1 + +/* Define to 1 if your system has a GNU libc compatible `calloc' function, and + to 0 otherwise. */ +#define HAVE_CALLOC_GNU 1 + +/* Define if the 'calloc' function is POSIX compliant. */ +#define HAVE_CALLOC_POSIX 1 + +/* Define to 1 if you have the `canonicalize_file_name' function. */ +#define HAVE_CANONICALIZE_FILE_NAME 1 + +/* Define to 1 if you have the `catgets' function. */ +#define HAVE_CATGETS 1 + +/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +/* #undef HAVE_CFLOCALECOPYCURRENT */ + +/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#define HAVE_DCGETTEXT 1 + +/* Define to 1 if you have the declaration of `alarm', and to 0 if you don't. + */ +#define HAVE_DECL_ALARM 1 + +/* Define to 1 if you have the declaration of `fpurge', and to 0 if you don't. + */ +#define HAVE_DECL_FPURGE 0 + +/* Define to 1 if you have the declaration of `fseeko', and to 0 if you don't. + */ +#define HAVE_DECL_FSEEKO 1 + +/* Define to 1 if you have the declaration of `ftello', and to 0 if you don't. + */ +#define HAVE_DECL_FTELLO 1 + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_GETC_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `getenv', and to 0 if you don't. + */ +#define HAVE_DECL_GETENV 1 + +/* Define to 1 if you have the declaration of `isblank', and to 0 if you + don't. */ +/* #undef HAVE_DECL_ISBLANK */ + +/* Define to 1 if you have the declaration of `program_invocation_name', and + to 0 if you don't. */ +#define HAVE_DECL_PROGRAM_INVOCATION_NAME 1 + +/* Define to 1 if you have the declaration of `program_invocation_short_name', + and to 0 if you don't. */ +#define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME 1 + +/* Define to 1 if you have the declaration of `setenv', and to 0 if you don't. + */ +#define HAVE_DECL_SETENV 1 + +/* Define to 1 if you have the declaration of `sleep', and to 0 if you don't. + */ +#define HAVE_DECL_SLEEP 1 + +/* Define to 1 if you have the declaration of `strdup', and to 0 if you don't. + */ +#define HAVE_DECL_STRDUP 1 + +/* Define to 1 if you have the declaration of `strerror', and to 0 if you + don't. */ +/* #undef HAVE_DECL_STRERROR */ + +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you + don't. */ +#define HAVE_DECL_STRERROR_R 1 + +/* Define to 1 if you have the declaration of `strndup', and to 0 if you + don't. */ +#define HAVE_DECL_STRNDUP 1 + +/* Define to 1 if you have the declaration of `strnlen', and to 0 if you + don't. */ +#define HAVE_DECL_STRNLEN 1 + +/* Define to 1 if you have the declaration of `unsetenv', and to 0 if you + don't. */ +#define HAVE_DECL_UNSETENV 1 + +/* Define to 1 if you have the declaration of `wctob', and to 0 if you don't. + */ +#define HAVE_DECL_WCTOB 1 + +/* Define to 1 if you have the declaration of `__fpending', and to 0 if you + don't. */ +#define HAVE_DECL___FPENDING 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `dup2' function. */ +#define HAVE_DUP2 1 + +/* Define if you have the declaration of environ. */ +#define HAVE_ENVIRON_DECL 1 + +/* Define to 1 if you have the `fcntl' function. */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the <features.h> header file. */ +#define HAVE_FEATURES_H 1 + +/* Define to 1 if you have the `fpurge' function. */ +/* #undef HAVE_FPURGE */ + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if you have the `fsync' function. */ +#define HAVE_FSYNC 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the <getopt.h> header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long_only' function. */ +#define HAVE_GETOPT_LONG_ONLY 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `getppriv' function. */ +/* #undef HAVE_GETPPRIV */ + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#define HAVE_GETTEXT 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `getuid' function. */ +#define HAVE_GETUID 1 + +/* Define if you have the iconv() function and it works. */ +/* #undef HAVE_ICONV */ + +/* Define to 1 if the compiler supports one of the keywords 'inline', + '__inline__', '__inline' and effectively inlines functions marked as such. + */ +#define HAVE_INLINE 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `isblank' function. */ +#define HAVE_ISBLANK 1 + +/* Define to 1 if you have the `iswcntrl' function. */ +#define HAVE_ISWCNTRL 1 + +/* Define to 1 if you have the `iswctype' function. */ +#define HAVE_ISWCTYPE 1 + +/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */ +#define HAVE_LANGINFO_CODESET 1 + +/* Define to 1 if you have the <langinfo.h> header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define if you have <langinfo.h> and nl_langinfo(YESEXPR). */ +/* #undef HAVE_LANGINFO_YESEXPR */ + +/* Define if your <locale.h> file defines LC_MESSAGES. */ +#define HAVE_LC_MESSAGES 1 + +/* Define to 1 if you have the <libintl.h> header file. */ +/* #undef HAVE_LIBINTL_H */ + +/* Define to 1 if you have the `parted' library (-lparted). */ +/* #undef HAVE_LIBPARTED */ + +/* have readline */ +#define HAVE_LIBREADLINE 1 + +/* Have libreiserfs */ +/* #undef HAVE_LIBREISERFS */ + +/* Define to 1 if the system has the type `long long int'. */ +#define HAVE_LONG_LONG_INT 1 + +/* Define to 1 if you have the `lstat' function. */ +#define HAVE_LSTAT 1 + +/* Define to 1 if your system has a GNU libc compatible 'malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC_GNU 1 + +/* Define if the 'malloc' function is POSIX compliant. */ +#define HAVE_MALLOC_POSIX 1 + +/* Define to 1 if mmap()'s MAP_ANONYMOUS flag is available after including + config.h and <sys/mman.h>. */ +#define HAVE_MAP_ANONYMOUS 1 + +/* Define to 1 if you have the `mbrtowc' function. */ +#define HAVE_MBRTOWC 1 + +/* Define to 1 if you have the `mbsinit' function. */ +#define HAVE_MBSINIT 1 + +/* Define to 1 if <wchar.h> declares mbstate_t. */ +#define HAVE_MBSTATE_T 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mkstemp' function. */ +#define HAVE_MKSTEMP 1 + +/* Define to 1 if you have the `mprotect' function. */ +#define HAVE_MPROTECT 1 + +/* Define to 1 if you have the `newlocale' function. */ +#define HAVE_NEWLOCALE 1 + +/* Define to 1 if you have the `nl_langinfo' function. */ +#define HAVE_NL_LANGINFO 1 + +/* Define to 1 if you have the <OS.h> header file. */ +/* #undef HAVE_OS_H */ + +/* Define to 1 if you have the `pathconf' function. */ +#define HAVE_PATHCONF 1 + +/* Define to 1 if you have the <priv.h> header file. */ +/* #undef HAVE_PRIV_H */ + +/* Define to 1 if you have the `pthread_atfork' function. */ +#define HAVE_PTHREAD_ATFORK 1 + +/* Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE. */ +#define HAVE_PTHREAD_MUTEX_RECURSIVE 1 + +/* Define if the POSIX multithreading library has read/write locks. */ +#define HAVE_PTHREAD_RWLOCK 1 + +/* Define to 1 if atoll is declared even after undefining macros. */ +#define HAVE_RAW_DECL_ATOLL 1 + +/* Define to 1 if btowc is declared even after undefining macros. */ +#define HAVE_RAW_DECL_BTOWC 1 + +/* Define to 1 if canonicalize_file_name is declared even after undefining + macros. */ +#define HAVE_RAW_DECL_CANONICALIZE_FILE_NAME 1 + +/* Define to 1 if chown is declared even after undefining macros. */ +#define HAVE_RAW_DECL_CHOWN 1 + +/* Define to 1 if dprintf is declared even after undefining macros. */ +#define HAVE_RAW_DECL_DPRINTF 1 + +/* Define to 1 if dup2 is declared even after undefining macros. */ +#define HAVE_RAW_DECL_DUP2 1 + +/* Define to 1 if dup3 is declared even after undefining macros. */ +#define HAVE_RAW_DECL_DUP3 1 + +/* Define to 1 if duplocale is declared even after undefining macros. */ +#define HAVE_RAW_DECL_DUPLOCALE 1 + +/* Define to 1 if endusershell is declared even after undefining macros. */ +#define HAVE_RAW_DECL_ENDUSERSHELL 1 + +/* Define to 1 if environ is declared even after undefining macros. */ +#define HAVE_RAW_DECL_ENVIRON 1 + +/* Define to 1 if euidaccess is declared even after undefining macros. */ +#define HAVE_RAW_DECL_EUIDACCESS 1 + +/* Define to 1 if faccessat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FACCESSAT 1 + +/* Define to 1 if fchdir is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FCHDIR 1 + +/* Define to 1 if fchmodat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FCHMODAT 1 + +/* Define to 1 if fchownat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FCHOWNAT 1 + +/* Define to 1 if fcntl is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FCNTL 1 + +/* Define to 1 if fpurge is declared even after undefining macros. */ +/* #undef HAVE_RAW_DECL_FPURGE */ + +/* Define to 1 if fseeko is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FSEEKO 1 + +/* Define to 1 if fstatat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FSTATAT 1 + +/* Define to 1 if fsync is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FSYNC 1 + +/* Define to 1 if ftello is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FTELLO 1 + +/* Define to 1 if ftruncate is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FTRUNCATE 1 + +/* Define to 1 if futimens is declared even after undefining macros. */ +#define HAVE_RAW_DECL_FUTIMENS 1 + +/* Define to 1 if getcwd is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETCWD 1 + +/* Define to 1 if getdelim is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETDELIM 1 + +/* Define to 1 if getdomainname is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETDOMAINNAME 1 + +/* Define to 1 if getdtablesize is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETDTABLESIZE 1 + +/* Define to 1 if getgroups is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETGROUPS 1 + +/* Define to 1 if gethostname is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETHOSTNAME 1 + +/* Define to 1 if getline is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETLINE 1 + +/* Define to 1 if getloadavg is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETLOADAVG 1 + +/* Define to 1 if getlogin is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETLOGIN 1 + +/* Define to 1 if getlogin_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETLOGIN_R 1 + +/* Define to 1 if getpagesize is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETPAGESIZE 1 + +/* Define to 1 if getsubopt is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETSUBOPT 1 + +/* Define to 1 if gettimeofday is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETTIMEOFDAY 1 + +/* Define to 1 if getusershell is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GETUSERSHELL 1 + +/* Define to 1 if grantpt is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GRANTPT 1 + +/* Define to 1 if group_member is declared even after undefining macros. */ +#define HAVE_RAW_DECL_GROUP_MEMBER 1 + +/* Define to 1 if imaxabs is declared even after undefining macros. */ +#define HAVE_RAW_DECL_IMAXABS 1 + +/* Define to 1 if imaxdiv is declared even after undefining macros. */ +#define HAVE_RAW_DECL_IMAXDIV 1 + +/* Define to 1 if initstat_r is declared even after undefining macros. */ +/* #undef HAVE_RAW_DECL_INITSTAT_R */ + +/* Define to 1 if iswctype is declared even after undefining macros. */ +#define HAVE_RAW_DECL_ISWCTYPE 1 + +/* Define to 1 if lchmod is declared even after undefining macros. */ +#define HAVE_RAW_DECL_LCHMOD 1 + +/* Define to 1 if lchown is declared even after undefining macros. */ +#define HAVE_RAW_DECL_LCHOWN 1 + +/* Define to 1 if link is declared even after undefining macros. */ +#define HAVE_RAW_DECL_LINK 1 + +/* Define to 1 if linkat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_LINKAT 1 + +/* Define to 1 if lseek is declared even after undefining macros. */ +#define HAVE_RAW_DECL_LSEEK 1 + +/* Define to 1 if lstat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_LSTAT 1 + +/* Define to 1 if mbrlen is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MBRLEN 1 + +/* Define to 1 if mbrtowc is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MBRTOWC 1 + +/* Define to 1 if mbsinit is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MBSINIT 1 + +/* Define to 1 if mbsnrtowcs is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MBSNRTOWCS 1 + +/* Define to 1 if mbsrtowcs is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MBSRTOWCS 1 + +/* Define to 1 if memmem is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MEMMEM 1 + +/* Define to 1 if mempcpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MEMPCPY 1 + +/* Define to 1 if memrchr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MEMRCHR 1 + +/* Define to 1 if mkdirat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKDIRAT 1 + +/* Define to 1 if mkdtemp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKDTEMP 1 + +/* Define to 1 if mkfifo is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKFIFO 1 + +/* Define to 1 if mkfifoat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKFIFOAT 1 + +/* Define to 1 if mknod is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKNOD 1 + +/* Define to 1 if mknodat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKNODAT 1 + +/* Define to 1 if mkostemp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKOSTEMP 1 + +/* Define to 1 if mkostemps is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKOSTEMPS 1 + +/* Define to 1 if mkstemp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKSTEMP 1 + +/* Define to 1 if mkstemps is declared even after undefining macros. */ +#define HAVE_RAW_DECL_MKSTEMPS 1 + +/* Define to 1 if nl_langinfo is declared even after undefining macros. */ +#define HAVE_RAW_DECL_NL_LANGINFO 1 + +/* Define to 1 if openat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_OPENAT 1 + +/* Define to 1 if pipe is declared even after undefining macros. */ +#define HAVE_RAW_DECL_PIPE 1 + +/* Define to 1 if pipe2 is declared even after undefining macros. */ +#define HAVE_RAW_DECL_PIPE2 1 + +/* Define to 1 if popen is declared even after undefining macros. */ +#define HAVE_RAW_DECL_POPEN 1 + +/* Define to 1 if pread is declared even after undefining macros. */ +#define HAVE_RAW_DECL_PREAD 1 + +/* Define to 1 if ptsname is declared even after undefining macros. */ +#define HAVE_RAW_DECL_PTSNAME 1 + +/* Define to 1 if pwrite is declared even after undefining macros. */ +#define HAVE_RAW_DECL_PWRITE 1 + +/* Define to 1 if random_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_RANDOM_R 1 + +/* Define to 1 if rawmemchr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_RAWMEMCHR 1 + +/* Define to 1 if readlink is declared even after undefining macros. */ +#define HAVE_RAW_DECL_READLINK 1 + +/* Define to 1 if readlinkat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_READLINKAT 1 + +/* Define to 1 if realpath is declared even after undefining macros. */ +#define HAVE_RAW_DECL_REALPATH 1 + +/* Define to 1 if renameat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_RENAMEAT 1 + +/* Define to 1 if rmdir is declared even after undefining macros. */ +#define HAVE_RAW_DECL_RMDIR 1 + +/* Define to 1 if rpmatch is declared even after undefining macros. */ +#define HAVE_RAW_DECL_RPMATCH 1 + +/* Define to 1 if setenv is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SETENV 1 + +/* Define to 1 if setlocale is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SETLOCALE 1 + +/* Define to 1 if setstate_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SETSTATE_R 1 + +/* Define to 1 if setusershell is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SETUSERSHELL 1 + +/* Define to 1 if sleep is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SLEEP 1 + +/* Define to 1 if snprintf is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SNPRINTF 1 + +/* Define to 1 if srandom_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SRANDOM_R 1 + +/* Define to 1 if stat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STAT 1 + +/* Define to 1 if stpcpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STPCPY 1 + +/* Define to 1 if stpncpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STPNCPY 1 + +/* Define to 1 if strcasestr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRCASESTR 1 + +/* Define to 1 if strchrnul is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRCHRNUL 1 + +/* Define to 1 if strdup is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRDUP 1 + +/* Define to 1 if strerror_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRERROR_R 1 + +/* Define to 1 if strncat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRNCAT 1 + +/* Define to 1 if strndup is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRNDUP 1 + +/* Define to 1 if strnlen is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRNLEN 1 + +/* Define to 1 if strpbrk is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRPBRK 1 + +/* Define to 1 if strsep is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRSEP 1 + +/* Define to 1 if strsignal is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRSIGNAL 1 + +/* Define to 1 if strtod is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRTOD 1 + +/* Define to 1 if strtoimax is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRTOIMAX 1 + +/* Define to 1 if strtok_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRTOK_R 1 + +/* Define to 1 if strtoll is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRTOLL 1 + +/* Define to 1 if strtoull is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRTOULL 1 + +/* Define to 1 if strtoumax is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRTOUMAX 1 + +/* Define to 1 if strverscmp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_STRVERSCMP 1 + +/* Define to 1 if symlink is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SYMLINK 1 + +/* Define to 1 if symlinkat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_SYMLINKAT 1 + +/* Define to 1 if tmpfile is declared even after undefining macros. */ +#define HAVE_RAW_DECL_TMPFILE 1 + +/* Define to 1 if towctrans is declared even after undefining macros. */ +#define HAVE_RAW_DECL_TOWCTRANS 1 + +/* Define to 1 if ttyname_r is declared even after undefining macros. */ +#define HAVE_RAW_DECL_TTYNAME_R 1 + +/* Define to 1 if unlink is declared even after undefining macros. */ +#define HAVE_RAW_DECL_UNLINK 1 + +/* Define to 1 if unlinkat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_UNLINKAT 1 + +/* Define to 1 if unlockpt is declared even after undefining macros. */ +#define HAVE_RAW_DECL_UNLOCKPT 1 + +/* Define to 1 if unsetenv is declared even after undefining macros. */ +#define HAVE_RAW_DECL_UNSETENV 1 + +/* Define to 1 if usleep is declared even after undefining macros. */ +#define HAVE_RAW_DECL_USLEEP 1 + +/* Define to 1 if utimensat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_UTIMENSAT 1 + +/* Define to 1 if vdprintf is declared even after undefining macros. */ +#define HAVE_RAW_DECL_VDPRINTF 1 + +/* Define to 1 if vsnprintf is declared even after undefining macros. */ +#define HAVE_RAW_DECL_VSNPRINTF 1 + +/* Define to 1 if wcpcpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCPCPY 1 + +/* Define to 1 if wcpncpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCPNCPY 1 + +/* Define to 1 if wcrtomb is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCRTOMB 1 + +/* Define to 1 if wcscasecmp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCASECMP 1 + +/* Define to 1 if wcscat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCAT 1 + +/* Define to 1 if wcschr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCHR 1 + +/* Define to 1 if wcscmp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCMP 1 + +/* Define to 1 if wcscoll is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCOLL 1 + +/* Define to 1 if wcscpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCPY 1 + +/* Define to 1 if wcscspn is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSCSPN 1 + +/* Define to 1 if wcsdup is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSDUP 1 + +/* Define to 1 if wcslen is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSLEN 1 + +/* Define to 1 if wcsncasecmp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSNCASECMP 1 + +/* Define to 1 if wcsncat is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSNCAT 1 + +/* Define to 1 if wcsncmp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSNCMP 1 + +/* Define to 1 if wcsncpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSNCPY 1 + +/* Define to 1 if wcsnlen is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSNLEN 1 + +/* Define to 1 if wcsnrtombs is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSNRTOMBS 1 + +/* Define to 1 if wcspbrk is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSPBRK 1 + +/* Define to 1 if wcsrchr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSRCHR 1 + +/* Define to 1 if wcsrtombs is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSRTOMBS 1 + +/* Define to 1 if wcsspn is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSSPN 1 + +/* Define to 1 if wcsstr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSSTR 1 + +/* Define to 1 if wcstok is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSTOK 1 + +/* Define to 1 if wcswidth is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSWIDTH 1 + +/* Define to 1 if wcsxfrm is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCSXFRM 1 + +/* Define to 1 if wctob is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCTOB 1 + +/* Define to 1 if wctrans is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCTRANS 1 + +/* Define to 1 if wctype is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCTYPE 1 + +/* Define to 1 if wcwidth is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WCWIDTH 1 + +/* Define to 1 if wmemchr is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WMEMCHR 1 + +/* Define to 1 if wmemcmp is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WMEMCMP 1 + +/* Define to 1 if wmemcpy is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WMEMCPY 1 + +/* Define to 1 if wmemmove is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WMEMMOVE 1 + +/* Define to 1 if wmemset is declared even after undefining macros. */ +#define HAVE_RAW_DECL_WMEMSET 1 + +/* Define to 1 if _Exit is declared even after undefining macros. */ +#define HAVE_RAW_DECL__EXIT 1 + +/* Define to 1 if you have the <readline/history.h> header file. */ +#define HAVE_READLINE_HISTORY_H 1 + +/* Define to 1 if you have the <readline/readline.h> header file. */ +#define HAVE_READLINE_READLINE_H 1 + +/* Define to 1 if you have the `readlink' function. */ +#define HAVE_READLINK 1 + +/* Define to 1 if your system has a GNU libc compatible 'realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC_GNU 1 + +/* Define if the 'realloc' function is POSIX compliant. */ +#define HAVE_REALLOC_POSIX 1 + +/* Define to 1 if you have the `realpath' function. */ +#define HAVE_REALPATH 1 + +/* Have reiserfs_fs_check() */ +/* #undef HAVE_REISERFS_FS_CHECK */ + +/* Define to 1 if you have the `rl_completion_matches' function. */ +#define HAVE_RL_COMPLETION_MATCHES 1 + +/* Define to 1 if you have the `rpmatch' function. */ +#define HAVE_RPMATCH 1 + +/* Define to 1 if you have the <search.h> header file. */ +#define HAVE_SEARCH_H 1 + +/* Define to 1 if you have the `setenv' function. */ +#define HAVE_SETENV 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if 'sig_atomic_t' is a signed integer type. */ +/* #undef HAVE_SIGNED_SIG_ATOMIC_T */ + +/* Define to 1 if 'wchar_t' is a signed integer type. */ +/* #undef HAVE_SIGNED_WCHAR_T */ + +/* Define to 1 if 'wint_t' is a signed integer type. */ +/* #undef HAVE_SIGNED_WINT_T */ + +/* Define to 1 if you have the `sleep' function. */ +#define HAVE_SLEEP 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio_ext.h> header file. */ +#define HAVE_STDIO_EXT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror_r' function. */ +#define HAVE_STRERROR_R 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#define HAVE_STRTOULL 1 + +/* Define to 1 if you have the `symlink' function. */ +#define HAVE_SYMLINK 1 + +/* Define to 1 if you have the <sys/bitypes.h> header file. */ +/* #undef HAVE_SYS_BITYPES_H */ + +/* Define to 1 if you have the <sys/inttypes.h> header file. */ +/* #undef HAVE_SYS_INTTYPES_H */ + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/timeb.h> header file. */ +/* #undef HAVE_SYS_TIMEB_H */ + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <termcap.h> header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define to 1 if you have the `tsearch' function. */ +#define HAVE_TSEARCH 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `unsetenv' function. */ +#define HAVE_UNSETENV 1 + +/* Define to 1 if the system has the type `unsigned long long int'. */ +#define HAVE_UNSIGNED_LONG_LONG_INT 1 + +/* Define to 1 if you have the `uselocale' function. */ +#define HAVE_USELOCALE 1 + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to 1 if you have the <wchar.h> header file. */ +#define HAVE_WCHAR_H 1 + +/* Define if you have the 'wchar_t' type. */ +#define HAVE_WCHAR_T 1 + +/* Define to 1 if you have the `wcrtomb' function. */ +#define HAVE_WCRTOMB 1 + +/* Define to 1 if you have the `wcscoll' function. */ +#define HAVE_WCSCOLL 1 + +/* Define to 1 if you have the `wctob' function. */ +#define HAVE_WCTOB 1 + +/* Define to 1 if you have the <wctype.h> header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the <winsock2.h> header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define if you have the 'wint_t' type. */ +#define HAVE_WINT_T 1 + +/* Define to 1 if O_NOATIME works. */ +#define HAVE_WORKING_O_NOATIME 1 + +/* Define to 1 if O_NOFOLLOW works. */ +#define HAVE_WORKING_O_NOFOLLOW 1 + +/* Define to 1 if you have the <xlocale.h> header file. */ +#define HAVE_XLOCALE_H 1 + +/* Define to 1 if the system has the type `_Bool'. */ +#define HAVE__BOOL 1 + +/* Define to 1 if you have the `_ftime' function. */ +/* #undef HAVE__FTIME */ + +/* Define to 1 if you have the `__fpending' function. */ +#define HAVE___FPENDING 1 + +/* Define to 1 if you have the `__fpurge' function. */ +#define HAVE___FPURGE 1 + +/* Define to 1 if you have the `__freading' function. */ +#define HAVE___FREADING 1 + +/* Define to 1 if you have the `__xpg_strerror_r' function. */ +#define HAVE___XPG_STRERROR_R 1 + +/* Extract low level special HFS(+) files for debugging purposes when using + the "check" command (NOT FOR PACKAGING) */ +/* #undef HFS_EXTRACT_FS */ + +/* Define to 1 if lseek does not detect pipes. */ +/* #undef LSEEK_PIPE_BROKEN */ + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* If malloc(0) is != NULL, define this to 1. Otherwise define this to 0. */ +#define MALLOC_0_IS_NONNULL 1 + +/* Define to a substitute value for mmap()'s MAP_ANONYMOUS flag. */ +/* #undef MAP_ANONYMOUS */ + +/* Define if the mbrtowc function has the NULL pwc argument bug. */ +/* #undef MBRTOWC_NULL_ARG1_BUG */ + +/* Define if the mbrtowc function has the NULL string argument bug. */ +/* #undef MBRTOWC_NULL_ARG2_BUG */ + +/* Define if the mbrtowc function does not return 0 for a NUL character. */ +/* #undef MBRTOWC_NUL_RETVAL_BUG */ + +/* Define if the mbrtowc function returns a wrong return value. */ +/* #undef MBRTOWC_RETVAL_BUG */ + +/* Define to 1 if assertions should be disabled. */ +/* #undef NDEBUG */ + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Define to 1 if open() fails to recognize a trailing slash. */ +/* #undef OPEN_TRAILING_SLASH_BUG */ + +/* Name of package */ +#define PACKAGE "parted" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "bug-parted@gnu.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GNU parted" + +/* String identifying the packager of this software */ +/* #undef PACKAGE_PACKAGER */ + +/* Packager info for bug reports (URL/e-mail/...) */ +/* #undef PACKAGE_PACKAGER_BUG_REPORTS */ + +/* Packager-specific version information */ +/* #undef PACKAGE_PACKAGER_VERSION */ + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GNU parted 2.4.14-077e" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "parted" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "http://www.gnu.org/software/parted/" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.4.14-077e" + +/* the number of pending output bytes on stream `fp' */ +/* #undef PENDING_OUTPUT_N_BYTES */ + +/* Define if <inttypes.h> exists and defines unusable PRI* macros. */ +/* #undef PRI_MACROS_BROKEN */ + +/* Define to the type that is the result of default argument promotions of + type mode_t. */ +#define PROMOTED_MODE_T mode_t + +/* Define if the pthread_in_use() detection is hard. */ +/* #undef PTHREAD_IN_USE_DETECTION_HARD */ + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'ptrdiff_t'. */ +/* #undef PTRDIFF_T_SUFFIX */ + +/* Define to 1 if readlink fails to recognize a trailing slash. */ +/* #undef READLINK_TRAILING_SLASH_BUG */ + +/* Disable all writing code */ +/* #undef READ_ONLY */ + +/* Define to 1 if stat needs help when passed a directory name with a trailing + slash */ +/* #undef REPLACE_FUNC_STAT_DIR */ + +/* Define to 1 if stat needs help when passed a file name with a trailing + slash */ +/* #undef REPLACE_FUNC_STAT_FILE */ + +/* Define if nl_langinfo exists but is overridden by gnulib. */ +/* #undef REPLACE_NL_LANGINFO */ + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'sig_atomic_t'. */ +/* #undef SIG_ATOMIC_T_SUFFIX */ + +/* The size of `off_t', as computed by sizeof. */ +#define SIZEOF_OFF_T 8 + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'size_t'. */ +/* #undef SIZE_T_SUFFIX */ + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r returns char *. */ +#define STRERROR_R_CHAR_P 1 + +/* Define to 1 if unlink (dir) cannot possibly succeed. */ +#define UNLINK_CANNOT_UNLINK_DIR 1 + +/* Define to 1 if unlink() on a parent directory may succeed */ +/* #undef UNLINK_PARENT_BUG */ + +/* Define if you have sufficient blkid support. */ +#define USE_BLKID 0 + +/* Define if the POSIX multithreading library can be used. */ +#define USE_POSIX_THREADS 1 + +/* Define if references to the POSIX multithreading library should be made + weak. */ +#define USE_POSIX_THREADS_WEAK 1 + +/* Define if the GNU Pth multithreading library can be used. */ +/* #undef USE_PTH_THREADS */ + +/* Define if references to the GNU Pth multithreading library should be made + weak. */ +/* #undef USE_PTH_THREADS_WEAK */ + +/* Define if the old Solaris multithreading library can be used. */ +/* #undef USE_SOLARIS_THREADS */ + +/* Define if references to the old Solaris multithreading library should be + made weak. */ +/* #undef USE_SOLARIS_THREADS_WEAK */ + +/* Define if the Win32 multithreading API can be used. */ +/* #undef USE_WIN32_THREADS */ + +/* Version number of package */ +#define VERSION "2.4.14-077e" + +/* Define to 1 if unsetenv returns void instead of int. */ +/* #undef VOID_UNSETENV */ + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'wchar_t'. */ +/* #undef WCHAR_T_SUFFIX */ + +/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type + 'wint_t'. */ +/* #undef WINT_T_SUFFIX */ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* enable compile-time and run-time bounds-checking, and some warnings */ +/* #undef _FORTIFY_SOURCE */ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define if you want regoff_t to be at least as wide POSIX requires. */ +/* #undef _REGEX_LARGE_OFFSETS */ + +/* Define to 500 only on HP-UX. */ +/* #undef _XOPEN_SOURCE */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Define to rpl_ if the getopt replacement functions and variables should be + used. */ +/* #undef __GETOPT_PREFIX */ + +/* Ensure that <stdint.h> defines the limit macros, since gnulib's + <inttypes.h> relies on them. */ +#if defined __cplusplus && !defined __STDC_LIMIT_MACROS && GL_TRIGGER_STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* A replacement for va_copy, if needed. */ +#define gl_va_copy(a,b) ((a) = (b)) + +/* Define to rpl_gmtime if the replacement function should be used. */ +/* #undef gmtime */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Work around a bug in Apple GCC 4.0.1 build 5465: In C99 mode, it supports + the ISO C 99 semantics of 'extern inline' (unlike the GNU C semantics of + earlier versions), but does not display it by setting __GNUC_STDC_INLINE__. + __APPLE__ && __MACH__ test for MacOS X. + __APPLE_CC__ tests for the Apple compiler and its version. + __STDC_VERSION__ tests for the C99 mode. */ +#if defined __APPLE__ && defined __MACH__ && __APPLE_CC__ >= 5465 && !defined __cplusplus && __STDC_VERSION__ >= 199901L && !defined __GNUC_STDC_INLINE__ +# define __GNUC_STDC_INLINE__ 1 +#endif + +/* Define to 1 if the compiler is checking for lint. */ +/* #undef lint */ + +/* Define to rpl_localtime if the replacement function should be used. */ +/* #undef localtime */ + +/* Define to a type if <wchar.h> does not define. */ +/* #undef mbstate_t */ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef mode_t */ + +/* Define to the type of st_nlink in struct stat, or a supertype. */ +/* #undef nlink_t */ + +/* Define to rpl_re_comp if the replacement should be used. */ +/* #undef re_comp */ + +/* Define to rpl_re_compile_fastmap if the replacement should be used. */ +/* #undef re_compile_fastmap */ + +/* Define to rpl_re_compile_pattern if the replacement should be used. */ +/* #undef re_compile_pattern */ + +/* Define to rpl_re_exec if the replacement should be used. */ +/* #undef re_exec */ + +/* Define to rpl_re_match if the replacement should be used. */ +/* #undef re_match */ + +/* Define to rpl_re_match_2 if the replacement should be used. */ +/* #undef re_match_2 */ + +/* Define to rpl_re_search if the replacement should be used. */ +/* #undef re_search */ + +/* Define to rpl_re_search_2 if the replacement should be used. */ +/* #undef re_search_2 */ + +/* Define to rpl_re_set_registers if the replacement should be used. */ +/* #undef re_set_registers */ + +/* Define to rpl_re_set_syntax if the replacement should be used. */ +/* #undef re_set_syntax */ + +/* Define to rpl_re_syntax_options if the replacement should be used. */ +/* #undef re_syntax_options */ + +/* Define to rpl_regcomp if the replacement should be used. */ +/* #undef regcomp */ + +/* Define to rpl_regerror if the replacement should be used. */ +/* #undef regerror */ + +/* Define to rpl_regexec if the replacement should be used. */ +/* #undef regexec */ + +/* Define to rpl_regfree if the replacement should be used. */ +/* #undef regfree */ + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#define restrict __restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef size_t */ + +/* Define as a signed type of the same size as size_t. */ +/* #undef ssize_t */ + +/* Define as a marker that can be attached to declarations that might not + be used. This helps to reduce warnings, such as from + GCC -Wunused-parameter. */ +#if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_UNUSED __attribute__ ((__unused__)) +#else +# define _GL_UNUSED +#endif +/* The name _UNUSED_PARAMETER_ is an earlier spelling, although the name + is a misnomer outside of parameter lists. */ +#define _UNUSED_PARAMETER_ _GL_UNUSED + + +/* Define to an unsigned 32-bit type if <sys/types.h> lacks this type. */ +/* #undef useconds_t */ + +/* Define as a macro for copying va_list variables. */ +/* #undef va_copy */ diff --git a/include/configmake.h b/include/configmake.h new file mode 100644 index 0000000..d70e510 --- /dev/null +++ b/include/configmake.h @@ -0,0 +1,27 @@ +/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ +#define PREFIX "/usr/local" +#define EXEC_PREFIX "/usr/local" +#define BINDIR "/usr/local/bin" +#define SBINDIR "/usr/local/sbin" +#define LIBEXECDIR "/usr/local/libexec" +#define DATAROOTDIR "/usr/local/share" +#define DATADIR "/usr/local/share" +#define SYSCONFDIR "/usr/local/etc" +#define SHAREDSTATEDIR "/usr/local/com" +#define LOCALSTATEDIR "/usr/local/var" +#define INCLUDEDIR "/usr/local/include" +#define OLDINCLUDEDIR "/usr/include" +#define DOCDIR "/usr/local/share/doc/parted" +#define INFODIR "/usr/local/share/info" +#define HTMLDIR "/usr/local/share/doc/parted" +#define DVIDIR "/usr/local/share/doc/parted" +#define PDFDIR "/usr/local/share/doc/parted" +#define PSDIR "/usr/local/share/doc/parted" +#define LIBDIR "/usr/local/lib" +#define LISPDIR "/usr/local/share/emacs/site-lisp" +#define LOCALEDIR "/usr/local/share/locale" +#define MANDIR "/usr/local/share/man" +#define PKGDATADIR "/usr/local/share/parted" +#define PKGINCLUDEDIR "/usr/local/include/parted" +#define PKGLIBDIR "/usr/local/lib/parted" +#define PKGLIBEXECDIR "/usr/local/libexec/parted" diff --git a/include/dirname.h b/include/dirname.h new file mode 100644 index 0000000..2ef9882 --- /dev/null +++ b/include/dirname.h @@ -0,0 +1,46 @@ +/* Take file names apart into directory and base names. + + Copyright (C) 1998, 2001, 2003-2006, 2009-2011 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef DIRNAME_H_ +# define DIRNAME_H_ 1 + +# include <stdbool.h> +# include <stddef.h> +# include "dosname.h" + +# ifndef DIRECTORY_SEPARATOR +# define DIRECTORY_SEPARATOR '/' +# endif + +# ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT +# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0 +# endif + +# if GNULIB_DIRNAME +char *base_name (char const *file); +char *dir_name (char const *file); +# endif + +char *mdir_name (char const *file); +size_t base_len (char const *file); +size_t dir_len (char const *file); +char *last_component (char const *file); + +bool strip_trailing_slashes (char *file); + +#endif /* not DIRNAME_H_ */ diff --git a/include/dosname.h b/include/dosname.h new file mode 100644 index 0000000..acdd03b --- /dev/null +++ b/include/dosname.h @@ -0,0 +1,53 @@ +/* File names on MS-DOS/Windows systems. + + Copyright (C) 2000-2001, 2004-2006, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + From Paul Eggert and Jim Meyering. */ + +#ifndef _DOSNAME_H +#define _DOSNAME_H + +#if (defined _WIN32 || defined __WIN32__ || \ + defined __MSDOS__ || defined __CYGWIN__ || \ + defined __EMX__ || defined __DJGPP__) + /* This internal macro assumes ASCII, but all hosts that support drive + letters use ASCII. */ +# define _IS_DRIVE_LETTER(C) (((unsigned int) (C) | ('a' - 'A')) - 'a' \ + <= 'z' - 'a') +# define FILE_SYSTEM_PREFIX_LEN(Filename) \ + (_IS_DRIVE_LETTER ((Filename)[0]) && (Filename)[1] == ':' ? 2 : 0) +# ifndef __CYGWIN__ +# define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 1 +# endif +# define ISSLASH(C) ((C) == '/' || (C) == '\\') +#else +# define FILE_SYSTEM_PREFIX_LEN(Filename) 0 +# define ISSLASH(C) ((C) == '/') +#endif + +#ifndef FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE +# define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 0 +#endif + +#if FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE +# define IS_ABSOLUTE_FILE_NAME(F) ISSLASH ((F)[FILE_SYSTEM_PREFIX_LEN (F)]) +# else +# define IS_ABSOLUTE_FILE_NAME(F) \ + (ISSLASH ((F)[0]) || FILE_SYSTEM_PREFIX_LEN (F) != 0) +#endif +#define IS_RELATIVE_FILE_NAME(F) (! IS_ABSOLUTE_FILE_NAME (F)) + +#endif /* DOSNAME_H_ */ diff --git a/include/parted/constraint.h b/include/parted/constraint.h new file mode 100644 index 0000000..4098835 --- /dev/null +++ b/include/parted/constraint.h @@ -0,0 +1,96 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_CONSTRAINT_H_INCLUDED +#define PED_CONSTRAINT_H_INCLUDED + +typedef struct _PedConstraint PedConstraint; + +#include <parted/device.h> +#include <parted/geom.h> +#include <parted/natmath.h> + +struct _PedConstraint { + PedAlignment* start_align; + PedAlignment* end_align; + PedGeometry* start_range; + PedGeometry* end_range; + PedSector min_size; + PedSector max_size; +}; + +extern int +ped_constraint_init ( + PedConstraint* constraint, + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size); + +extern PedConstraint* +ped_constraint_new ( + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size); + +extern PedConstraint* +ped_constraint_new_from_min_max ( + const PedGeometry* min, + const PedGeometry* max); + +extern PedConstraint* +ped_constraint_new_from_min (const PedGeometry* min); + +extern PedConstraint* +ped_constraint_new_from_max (const PedGeometry* max); + +extern PedConstraint* +ped_constraint_duplicate (const PedConstraint* constraint); + +extern void +ped_constraint_done (PedConstraint* constraint); + +extern void +ped_constraint_destroy (PedConstraint* constraint); + +extern PedConstraint* +ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b); + +extern PedGeometry* +ped_constraint_solve_max (const PedConstraint* constraint); + +extern PedGeometry* +ped_constraint_solve_nearest ( + const PedConstraint* constraint, const PedGeometry* geom); + +extern int +ped_constraint_is_solution (const PedConstraint* constraint, + const PedGeometry* geom); + +extern PedConstraint* +ped_constraint_any (const PedDevice* dev); + +extern PedConstraint* +ped_constraint_exact (const PedGeometry* geom); + +#endif /* PED_CONSTRAINT_H_INCLUDED */ diff --git a/include/parted/debug.h b/include/parted/debug.h new file mode 100644 index 0000000..71372d4 --- /dev/null +++ b/include/parted/debug.h @@ -0,0 +1,88 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1998-2000, 2002, 2007, 2009-2011 Free Software Foundation, + Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_DEBUG_H_INCLUDED +#define PED_DEBUG_H_INCLUDED + +#include <stdarg.h> + +#ifdef DEBUG + +typedef void (PedDebugHandler) ( const int level, const char* file, int line, + const char* function, const char* msg ); + +extern void ped_debug_set_handler (PedDebugHandler* handler); +extern void ped_debug ( const int level, const char* file, int line, + const char* function, const char* msg, ... ); + +extern void __attribute__((__noreturn__)) +ped_assert ( const char* cond_text, + const char* file, int line, const char* function ); + +#if defined __GNUC__ && !defined __JSFTRACE__ + +#define PED_DEBUG(level, ...) \ + ped_debug ( level, __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + __VA_ARGS__ ) + +#define PED_ASSERT(cond) \ + do { \ + if (!(cond)) { \ + ped_assert ( \ + #cond, \ + __FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__ ); \ + } \ + } while (0) + +#else /* !__GNUC__ */ + +/* function because variadic macros are C99 */ +static void PED_DEBUG (int level, ...) +{ + va_list va_args; + + va_start (va_args, level); + ped_debug ( level, "unknown file", 0, "unknown function", va_args ); + va_end (va_args); +} + +#define PED_ASSERT(cond) \ + do { \ + if (!(cond)) { \ + ped_assert ( \ + #cond, \ + "unknown", \ + 0, \ + "unknown"); \ + } \ + } while (0) + +#endif /* __GNUC__ */ + +#else /* !DEBUG */ + +#define PED_ASSERT(cond) do {} while (0) +#define PED_DEBUG(level, ...) do {} while (0) + + +#endif /* DEBUG */ + +#endif /* PED_DEBUG_H_INCLUDED */ diff --git a/include/parted/device.h b/include/parted/device.h new file mode 100644 index 0000000..0634465 --- /dev/null +++ b/include/parted/device.h @@ -0,0 +1,165 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1998 - 2001, 2005, 2007-2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedDevice + * @{ + */ + +/** \file device.h */ + +#ifndef PED_DEVICE_H_INCLUDED +#define PED_DEVICE_H_INCLUDED + +/** We can address 2^63 sectors */ +typedef long long PedSector; + +/** \deprecated Removal from API planned */ +typedef enum { + PED_DEVICE_UNKNOWN = 0, + PED_DEVICE_SCSI = 1, + PED_DEVICE_IDE = 2, + PED_DEVICE_DAC960 = 3, + PED_DEVICE_CPQARRAY = 4, + PED_DEVICE_FILE = 5, + PED_DEVICE_ATARAID = 6, + PED_DEVICE_I2O = 7, + PED_DEVICE_UBD = 8, + PED_DEVICE_DASD = 9, + PED_DEVICE_VIODASD = 10, + PED_DEVICE_SX8 = 11, + PED_DEVICE_DM = 12, + PED_DEVICE_XVD = 13, + PED_DEVICE_SDMMC = 14, + PED_DEVICE_VIRTBLK = 15, + PED_DEVICE_AOE = 16, + PED_DEVICE_MD = 17 +} PedDeviceType; + +typedef struct _PedDevice PedDevice; +typedef struct _PedDeviceArchOps PedDeviceArchOps; +typedef struct _PedCHSGeometry PedCHSGeometry; + +/** + * A cylinder-head-sector "old-style" geometry. + * + * A device addressed in this way has C*H*S sectors. + */ +struct _PedCHSGeometry { + int cylinders; + int heads; + int sectors; +}; + +/** A block device - for example, /dev/hda, not /dev/hda3 */ +struct _PedDevice { + PedDevice* next; + + char* model; /**< \brief description of hardware + (manufacturer, model) */ + char* path; /**< device /dev entry */ + + PedDeviceType type; /**< SCSI, IDE, etc. + \deprecated \sa PedDeviceType */ + long long sector_size; /**< logical sector size */ + long long phys_sector_size; /**< physical sector size */ + PedSector length; /**< device length (LBA) */ + + int open_count; /**< the number of times this device has + been opened with ped_device_open(). */ + int read_only; + int external_mode; + int dirty; + int boot_dirty; + + PedCHSGeometry hw_geom; + PedCHSGeometry bios_geom; + short host, did; + + void* arch_specific; +}; + +#include <parted/natmath.h> + +/** + * List of functions implementing architecture-specific operations. + */ +struct _PedDeviceArchOps { + PedDevice* (*_new) (const char* path); + void (*destroy) (PedDevice* dev); + int (*is_busy) (PedDevice* dev); + int (*open) (PedDevice* dev); + int (*refresh_open) (PedDevice* dev); + int (*close) (PedDevice* dev); + int (*refresh_close) (PedDevice* dev); + int (*read) (const PedDevice* dev, void* buffer, + PedSector start, PedSector count); + int (*write) (PedDevice* dev, const void* buffer, + PedSector start, PedSector count); + int (*sync) (PedDevice* dev); + int (*sync_fast) (PedDevice* dev); + PedSector (*check) (PedDevice* dev, void* buffer, + PedSector start, PedSector count); + void (*probe_all) (); + /* These functions are optional */ + PedAlignment *(*get_minimum_alignment)(const PedDevice *dev); + PedAlignment *(*get_optimum_alignment)(const PedDevice *dev); +}; + +#include <parted/constraint.h> +#include <parted/timer.h> + +extern void ped_device_probe_all (); +extern void ped_device_free_all (); + +extern PedDevice* ped_device_get (const char* name); +extern PedDevice* ped_device_get_next (const PedDevice* dev); +extern int ped_device_is_busy (PedDevice* dev); +extern int ped_device_open (PedDevice* dev); +extern int ped_device_close (PedDevice* dev); +extern void ped_device_destroy (PedDevice* dev); +extern void ped_device_cache_remove (PedDevice* dev); + +extern int ped_device_begin_external_access (PedDevice* dev); +extern int ped_device_end_external_access (PedDevice* dev); + +extern int ped_device_read (const PedDevice* dev, void* buffer, + PedSector start, PedSector count); +extern int ped_device_write (PedDevice* dev, const void* buffer, + PedSector start, PedSector count); +extern int ped_device_sync (PedDevice* dev); +extern int ped_device_sync_fast (PedDevice* dev); +extern PedSector ped_device_check (PedDevice* dev, void* buffer, + PedSector start, PedSector count); +extern PedConstraint* ped_device_get_constraint (const PedDevice* dev); + +extern PedConstraint *ped_device_get_minimal_aligned_constraint( + const PedDevice *dev); +extern PedConstraint *ped_device_get_optimal_aligned_constraint( + const PedDevice *dev); + +extern PedAlignment *ped_device_get_minimum_alignment(const PedDevice *dev); +extern PedAlignment *ped_device_get_optimum_alignment(const PedDevice *dev); + +/* private stuff ;-) */ + +extern void _ped_device_probe (const char* path); + +#endif /* PED_DEVICE_H_INCLUDED */ + +/** @} */ diff --git a/include/parted/disk.h b/include/parted/disk.h new file mode 100644 index 0000000..364ccaf --- /dev/null +++ b/include/parted/disk.h @@ -0,0 +1,389 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2002, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedDisk + * @{ + */ + +/** \file disk.h */ + +#ifndef PED_DISK_H_INCLUDED +#define PED_DISK_H_INCLUDED + +/** + * Disk flags + */ +enum _PedDiskFlag { + /* This flag (which defaults to true) controls if disk types for + which cylinder alignment is optional do cylinder alignment when a + new partition gets added. + This flag is available for msdos and sun disklabels (for sun labels + it only controls the aligning of the end of the partition) */ + PED_DISK_CYLINDER_ALIGNMENT=1, +}; +#define PED_DISK_FIRST_FLAG PED_DISK_CYLINDER_ALIGNMENT +#define PED_DISK_LAST_FLAG PED_DISK_CYLINDER_ALIGNMENT + +/** + * Partition types + */ +enum _PedPartitionType { + PED_PARTITION_NORMAL = 0x00, + PED_PARTITION_LOGICAL = 0x01, + PED_PARTITION_EXTENDED = 0x02, + PED_PARTITION_FREESPACE = 0x04, + PED_PARTITION_METADATA = 0x08, + PED_PARTITION_PROTECTED = 0x10 +}; + +/** + * Partition flags. + */ +enum _PedPartitionFlag { + PED_PARTITION_BOOT=1, + PED_PARTITION_ROOT=2, + PED_PARTITION_SWAP=3, + PED_PARTITION_HIDDEN=4, + PED_PARTITION_RAID=5, + PED_PARTITION_LVM=6, + PED_PARTITION_LBA=7, + PED_PARTITION_HPSERVICE=8, + PED_PARTITION_PALO=9, + PED_PARTITION_PREP=10, + PED_PARTITION_MSFT_RESERVED=11, + PED_PARTITION_BIOS_GRUB=12, + PED_PARTITION_APPLE_TV_RECOVERY=13, + PED_PARTITION_DIAG=14, + PED_PARTITION_LEGACY_BOOT=15 +}; +#define PED_PARTITION_FIRST_FLAG PED_PARTITION_BOOT +#define PED_PARTITION_LAST_FLAG PED_PARTITION_LEGACY_BOOT + +enum _PedDiskTypeFeature { + PED_DISK_TYPE_EXTENDED=1, /**< supports extended partitions */ + PED_DISK_TYPE_PARTITION_NAME=2 /**< supports partition names */ +}; +#define PED_DISK_TYPE_FIRST_FEATURE PED_DISK_TYPE_EXTENDED +#define PED_DISK_TYPE_LAST_FEATURE PED_DISK_TYPE_PARTITION_NAME + +struct _PedDisk; +struct _PedPartition; +struct _PedDiskOps; +struct _PedDiskType; +struct _PedDiskArchOps; + +typedef enum _PedDiskFlag PedDiskFlag; +typedef enum _PedPartitionType PedPartitionType; +typedef enum _PedPartitionFlag PedPartitionFlag; +typedef enum _PedDiskTypeFeature PedDiskTypeFeature; +typedef struct _PedDisk PedDisk; +typedef struct _PedPartition PedPartition; +typedef const struct _PedDiskOps PedDiskOps; +typedef struct _PedDiskType PedDiskType; +typedef const struct _PedDiskArchOps PedDiskArchOps; + +#include <parted/device.h> +#include <parted/filesys.h> +#include <parted/natmath.h> +#include <parted/geom.h> +#include <stdbool.h> + +/** @} */ + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** \file disk.h */ + +/** + * PedPartition structure represents a partition. + */ +struct _PedPartition { + PedPartition* prev; + PedPartition* next; + + /**< the partition table of the partition */ + PedDisk* disk; + PedGeometry geom; /**< geometry of the partition */ + + /**< the partition number: In Linux, this is the + same as the minor number. No assumption + should be made about "num" and "type" + - different disk labels have different rules. */ + + int num; + PedPartitionType type; /**< the type of partition: a bit field of + PED_PARTITION_LOGICAL, PED_PARTITION_EXTENDED, + PED_PARTITION_METADATA + and PED_PARTITION_FREESPACE. + Both the first two, and the last two are + mutually exclusive. + An extended partition is a primary + partition that may contain logical partitions. + There is at most one extended partition on + a disk. + A logical partition is like a primary + partition, except it's inside an extended + partition. Internally, pseudo partitions are + allocated to represent free space, or disk + label meta-data. These have the + PED_PARTITION_FREESPACE or + PED_PARTITION_METADATA bit set. */ + + /**< The type of file system on the partition. NULL if unknown. */ + const PedFileSystemType* fs_type; + + /**< Only used for an extended partition. The list of logical + partitions (and free space and metadata within the extended + partition). */ + PedPartition* part_list; + + void* disk_specific; +}; + +/** @} */ + +/** + * \addtogroup PedDisk + * @{ + */ + +/** + * Represents a disk label (partition table). + */ +struct _PedDisk { + PedDevice* dev; /**< the device where the + partition table lies */ + const PedDiskType* type; /**< type of disk label */ + const int* block_sizes; /**< block sizes supported + by this label */ + PedPartition* part_list; /**< list of partitions. Access with + ped_disk_next_partition() */ + + void* disk_specific; + int update_mode; /**< mode without free/metadata + partitions, for easier + update */ +}; + +struct _PedDiskOps { + /* disk label operations */ + int (*probe) (const PedDevice *dev); + int (*clobber) (PedDevice* dev); + PedDisk* (*alloc) (const PedDevice* dev); + PedDisk* (*duplicate) (const PedDisk* disk); + void (*free) (PedDisk* disk); + int (*read) (PedDisk* disk); + int (*write) (const PedDisk* disk); + int (*disk_set_flag) ( + PedDisk *disk, + PedDiskFlag flag, + int state); + int (*disk_get_flag) ( + const PedDisk *disk, + PedDiskFlag flag); + int (*disk_is_flag_available) ( + const PedDisk *disk, + PedDiskFlag flag); + /** \todo add label guessing op here */ + + /* partition operations */ + PedPartition* (*partition_new) ( + const PedDisk* disk, + PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, + PedSector end); + PedPartition* (*partition_duplicate) (const PedPartition* part); + void (*partition_destroy) (PedPartition* part); + int (*partition_set_system) (PedPartition* part, + const PedFileSystemType* fs_type); + int (*partition_set_flag) ( + PedPartition* part, + PedPartitionFlag flag, + int state); + int (*partition_get_flag) ( + const PedPartition* part, + PedPartitionFlag flag); + int (*partition_is_flag_available) ( + const PedPartition* part, + PedPartitionFlag flag); + void (*partition_set_name) (PedPartition* part, const char* name); + const char* (*partition_get_name) (const PedPartition* part); + int (*partition_align) (PedPartition* part, + const PedConstraint* constraint); + int (*partition_enumerate) (PedPartition* part); + bool (*partition_check) (const PedPartition* part); + + /* other */ + int (*alloc_metadata) (PedDisk* disk); + int (*get_max_primary_partition_count) (const PedDisk* disk); + bool (*get_max_supported_partition_count) (const PedDisk* disk, + int* supported); + PedAlignment *(*get_partition_alignment)(const PedDisk *disk); + PedSector (*max_length) (void); + PedSector (*max_start_sector) (void); +}; + +struct _PedDiskType { + PedDiskType* next; + const char* name; /**< the name of the partition table type. + \todo not very intuitive name */ + PedDiskOps* const ops; + + PedDiskTypeFeature features; /**< bitmap of supported features */ +}; + +/** + * Architecture-specific operations. i.e. communication with kernel (or + * whatever) about changes, etc. + */ +struct _PedDiskArchOps { + char* (*partition_get_path) (const PedPartition* part); + int (*partition_is_busy) (const PedPartition* part); + int (*disk_commit) (PedDisk* disk); +}; + +extern void ped_disk_type_register (PedDiskType* type); +extern void ped_disk_type_unregister (PedDiskType* type); + +extern PedDiskType* ped_disk_type_get_next (PedDiskType const *type); +extern PedDiskType* ped_disk_type_get (const char* name); +extern int ped_disk_type_check_feature (const PedDiskType* disk_type, + PedDiskTypeFeature feature); + +extern PedDiskType* ped_disk_probe (PedDevice* dev); +extern PedDisk* ped_disk_new (PedDevice* dev); +extern PedDisk* ped_disk_duplicate (const PedDisk* old_disk); +extern void ped_disk_destroy (PedDisk* disk); +extern int ped_disk_commit (PedDisk* disk); +extern int ped_disk_commit_to_dev (PedDisk* disk); +extern int ped_disk_commit_to_os (PedDisk* disk); +extern int ped_disk_check (const PedDisk* disk); +extern void ped_disk_print (const PedDisk* disk); + +extern int ped_disk_get_primary_partition_count (const PedDisk* disk); +extern int ped_disk_get_last_partition_num (const PedDisk* disk); +extern int ped_disk_get_max_primary_partition_count (const PedDisk* disk); +extern bool ped_disk_get_max_supported_partition_count(const PedDisk* disk, + int* supported); +extern PedAlignment *ped_disk_get_partition_alignment(const PedDisk *disk); + +extern int ped_disk_set_flag(PedDisk *disk, PedDiskFlag flag, int state); +extern int ped_disk_get_flag(const PedDisk *disk, PedDiskFlag flag); +extern int ped_disk_is_flag_available(const PedDisk *disk, PedDiskFlag flag); + +extern const char *ped_disk_flag_get_name(PedDiskFlag flag); +extern PedDiskFlag ped_disk_flag_get_by_name(const char *name); +extern PedDiskFlag ped_disk_flag_next(PedDiskFlag flag); + +/** @} */ + +/** + * \addtogroup PedPartition + * + * @{ + */ + +extern PedPartition* ped_partition_new (const PedDisk* disk, + PedPartitionType type, + const PedFileSystemType* fs_type, + PedSector start, + PedSector end); +extern void ped_partition_destroy (PedPartition* part); +extern int ped_partition_is_active (const PedPartition* part); +extern int ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, + int state); +extern int ped_partition_get_flag (const PedPartition* part, + PedPartitionFlag flag); +extern int ped_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag); +extern int ped_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type); +extern int ped_partition_set_name (PedPartition* part, const char* name); +extern const char* ped_partition_get_name (const PedPartition* part); +extern int ped_partition_is_busy (const PedPartition* part); +extern char* ped_partition_get_path (const PedPartition* part); + +extern const char* ped_partition_type_get_name (PedPartitionType part_type); +extern const char* ped_partition_flag_get_name (PedPartitionFlag flag); +extern PedPartitionFlag ped_partition_flag_get_by_name (const char* name); +extern PedPartitionFlag ped_partition_flag_next (PedPartitionFlag flag); + +/** @} */ + +/** + * \addtogroup PedDisk + * @{ + */ + +extern int ped_disk_add_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint); +extern int ped_disk_remove_partition (PedDisk* disk, PedPartition* part); +extern int ped_disk_delete_partition (PedDisk* disk, PedPartition* part); +extern int ped_disk_delete_all (PedDisk* disk); +extern int ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint, + PedSector start, PedSector end); +extern int ped_disk_maximize_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint); +extern PedGeometry* ped_disk_get_max_partition_geometry (PedDisk* disk, + PedPartition* part, const PedConstraint* constraint); +extern int ped_disk_minimize_extended_partition (PedDisk* disk); + +extern PedPartition* ped_disk_next_partition (const PedDisk* disk, + const PedPartition* part); +extern PedPartition* ped_disk_get_partition (const PedDisk* disk, int num); +extern PedPartition* ped_disk_get_partition_by_sector (const PedDisk* disk, + PedSector sect); +extern PedPartition* ped_disk_extended_partition (const PedDisk* disk); + +extern PedSector ped_disk_max_partition_length (const PedDisk *disk); +extern PedSector ped_disk_max_partition_start_sector (const PedDisk *disk); + +/* internal functions */ +extern PedDisk* _ped_disk_alloc (const PedDevice* dev, const PedDiskType* type); +extern void _ped_disk_free (PedDisk* disk); + + +/** @} */ + +/** + * \addtogroup PedPartition + * + * @{ + */ + +extern PedPartition* _ped_partition_alloc (const PedDisk* disk, + PedPartitionType type, + const PedFileSystemType* fs_type, + PedSector start, + PedSector end); +extern void _ped_partition_free (PedPartition* part); + +extern int _ped_partition_attempt_align ( + PedPartition* part, const PedConstraint* external, + PedConstraint* internal); + +#endif /* PED_DISK_H_INCLUDED */ + +/** @} */ diff --git a/include/parted/endian.h b/include/parted/endian.h new file mode 100644 index 0000000..1a6a267 --- /dev/null +++ b/include/parted/endian.h @@ -0,0 +1,84 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1998-2002, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* should only be #included by files in libparted */ + +#ifndef PED_ENDIAN_H_INCLUDED +#define PED_ENDIAN_H_INCLUDED + +#include <stdint.h> + +/* returns the n'th least significant byte */ +#define _GET_BYTE(x, n) ( ((x) >> (8 * (n))) & 0xff ) + +#define _PED_SWAP16(x) ( (_GET_BYTE(x, 0) << 8) \ + + (_GET_BYTE(x, 1) << 0) ) + +#define _PED_SWAP32(x) ( (_GET_BYTE(x, 0) << 24) \ + + (_GET_BYTE(x, 1) << 16) \ + + (_GET_BYTE(x, 2) << 8) \ + + (_GET_BYTE(x, 3) << 0) ) + +#define _PED_SWAP64(x) ( (_GET_BYTE(x, 0) << 56) \ + + (_GET_BYTE(x, 1) << 48) \ + + (_GET_BYTE(x, 2) << 40) \ + + (_GET_BYTE(x, 3) << 32) \ + + (_GET_BYTE(x, 4) << 24) \ + + (_GET_BYTE(x, 5) << 16) \ + + (_GET_BYTE(x, 6) << 8) \ + + (_GET_BYTE(x, 7) << 0) ) + +#define PED_SWAP16(x) ((uint16_t) _PED_SWAP16( (uint16_t) (x) )) +#define PED_SWAP32(x) ((uint32_t) _PED_SWAP32( (uint32_t) (x) )) +#define PED_SWAP64(x) ((uint64_t) _PED_SWAP64( (uint64_t) (x) )) + +#ifdef WORDS_BIGENDIAN + +#define PED_CPU_TO_LE16(x) PED_SWAP16(x) +#define PED_CPU_TO_BE16(x) (x) +#define PED_CPU_TO_LE32(x) PED_SWAP32(x) +#define PED_CPU_TO_BE32(x) (x) +#define PED_CPU_TO_LE64(x) PED_SWAP64(x) +#define PED_CPU_TO_BE64(x) (x) + +#define PED_LE16_TO_CPU(x) PED_SWAP16(x) +#define PED_BE16_TO_CPU(x) (x) +#define PED_LE32_TO_CPU(x) PED_SWAP32(x) +#define PED_BE32_TO_CPU(x) (x) +#define PED_LE64_TO_CPU(x) PED_SWAP64(x) +#define PED_BE64_TO_CPU(x) (x) + +#else /* !WORDS_BIGENDIAN */ + +#define PED_CPU_TO_LE16(x) (x) +#define PED_CPU_TO_BE16(x) PED_SWAP16(x) +#define PED_CPU_TO_LE32(x) (x) +#define PED_CPU_TO_BE32(x) PED_SWAP32(x) +#define PED_CPU_TO_LE64(x) (x) +#define PED_CPU_TO_BE64(x) PED_SWAP64(x) + +#define PED_LE16_TO_CPU(x) (x) +#define PED_BE16_TO_CPU(x) PED_SWAP16(x) +#define PED_LE32_TO_CPU(x) (x) +#define PED_BE32_TO_CPU(x) PED_SWAP32(x) +#define PED_LE64_TO_CPU(x) (x) +#define PED_BE64_TO_CPU(x) PED_SWAP64(x) + +#endif /* !WORDS_BIGENDIAN */ + +#endif /* PED_ENDIAN_H_INCLUDED */ diff --git a/include/parted/exception.h b/include/parted/exception.h new file mode 100644 index 0000000..bfa6eb1 --- /dev/null +++ b/include/parted/exception.h @@ -0,0 +1,115 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedException + * @{ + */ + +/** \file exception.h */ + +#ifndef PED_EXCEPTION_H_INCLUDED +#define PED_EXCEPTION_H_INCLUDED + +typedef struct _PedException PedException; + +/** + * Exception type + */ +enum _PedExceptionType { + PED_EXCEPTION_INFORMATION=1, + PED_EXCEPTION_WARNING=2, + PED_EXCEPTION_ERROR=3, + PED_EXCEPTION_FATAL=4, + PED_EXCEPTION_BUG=5, + PED_EXCEPTION_NO_FEATURE=6, +}; +typedef enum _PedExceptionType PedExceptionType; + +/** + * Option for resolving the exception + */ +enum _PedExceptionOption { + PED_EXCEPTION_UNHANDLED=0, + PED_EXCEPTION_FIX=1, + PED_EXCEPTION_YES=2, + PED_EXCEPTION_NO=4, + PED_EXCEPTION_OK=8, + PED_EXCEPTION_RETRY=16, + PED_EXCEPTION_IGNORE=32, + PED_EXCEPTION_CANCEL=64, +}; +typedef enum _PedExceptionOption PedExceptionOption; +#define PED_EXCEPTION_OK_CANCEL (PED_EXCEPTION_OK + PED_EXCEPTION_CANCEL) +#define PED_EXCEPTION_YES_NO (PED_EXCEPTION_YES + PED_EXCEPTION_NO) +#define PED_EXCEPTION_YES_NO_CANCEL (PED_EXCEPTION_YES_NO \ + + PED_EXCEPTION_CANCEL) +#define PED_EXCEPTION_IGNORE_CANCEL (PED_EXCEPTION_IGNORE \ + + PED_EXCEPTION_CANCEL) +#define PED_EXCEPTION_RETRY_CANCEL (PED_EXCEPTION_RETRY + PED_EXCEPTION_CANCEL) +#define PED_EXCEPTION_RETRY_IGNORE_CANCEL (PED_EXCEPTION_RETRY \ + + PED_EXCEPTION_IGNORE_CANCEL) +#define PED_EXCEPTION_OPTION_FIRST PED_EXCEPTION_FIX +#define PED_EXCEPTION_OPTION_LAST PED_EXCEPTION_CANCEL + +/** + * Structure with information about exception + */ +struct _PedException { + char* message; /**< text describing what the event was */ + PedExceptionType type; /**< type of exception */ + PedExceptionOption options; /**< ORed list of options that + the exception handler can + return (the ways an exception + can be resolved) */ +}; + +typedef PedExceptionOption (PedExceptionHandler) (PedException* ex); + +extern int ped_exception; /* set to true if there's an exception */ + +extern char* ped_exception_get_type_string (PedExceptionType ex_type); +extern char* ped_exception_get_option_string (PedExceptionOption ex_opt); + +extern void ped_exception_set_handler (PedExceptionHandler* handler); +extern PedExceptionHandler *ped_exception_get_handler(void); + +extern PedExceptionOption ped_exception_default_handler (PedException* ex); + +extern PedExceptionOption ped_exception_throw (PedExceptionType ex_type, + PedExceptionOption ex_opt, + const char* message, + ...); +/* rethrows an exception - i.e. calls the exception handler, (or returns a + code to return to pass up higher) */ +extern PedExceptionOption ped_exception_rethrow (); + +/* frees an exception, indicating that the exception has been handled. + Calling an exception handler counts. */ +extern void ped_exception_catch (); + +/* indicate that exceptions should not go to the exception handler, but passed + up to the calling function(s) */ +extern void ped_exception_fetch_all (); + +/* indicate that exceptions should invoke the exception handler */ +extern void ped_exception_leave_all (); + +#endif /* PED_EXCEPTION_H_INCLUDED */ + +/** @} */ diff --git a/include/parted/filesys.h b/include/parted/filesys.h new file mode 100644 index 0000000..d69a22e --- /dev/null +++ b/include/parted/filesys.h @@ -0,0 +1,135 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2001, 2006-2007, 2009-2011 Free Software Foundation, + Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedFileSystem + * @{ + */ + +/** \file filesys.h */ + +#ifndef PED_FILESYS_H_INCLUDED +#define PED_FILESYS_H_INCLUDED + +typedef struct _PedFileSystem PedFileSystem; +typedef struct _PedFileSystemType PedFileSystemType; +typedef struct _PedFileSystemAlias PedFileSystemAlias; +typedef const struct _PedFileSystemOps PedFileSystemOps; + +#include <parted/geom.h> +#include <parted/constraint.h> +#include <parted/timer.h> + +struct _PedFileSystemOps { + PedGeometry* (*probe) (PedGeometry* geom); + int (*clobber) (PedGeometry* geom); + + PedFileSystem* (*open) (PedGeometry* geom); + PedFileSystem* (*create) (PedGeometry* geom, PedTimer* timer); + int (*close) (PedFileSystem* fs); + int (*check) (PedFileSystem* fs, PedTimer* timer); + PedFileSystem* (*copy) (const PedFileSystem* fs, PedGeometry* geom, + PedTimer* timer); + int (*resize) (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer); + + PedConstraint* (*get_create_constraint) (const PedDevice* dev); + PedConstraint* (*get_resize_constraint) (const PedFileSystem* fs); + PedConstraint* (*get_copy_constraint) (const PedFileSystem* fs, + const PedDevice* dev); +}; + +/** + * Structure describing type of file system + */ +struct _PedFileSystemType { + PedFileSystemType* next; + const char* const name; /**< name of the file system type */ + const int* block_sizes; + PedFileSystemOps* const ops; +}; + +/** + * Structure describing a file system alias. This is separate from + * PedFileSystemType because probing only looks through the list of types, + * and does not probe aliases separately. + */ +struct _PedFileSystemAlias { + PedFileSystemAlias* next; + PedFileSystemType* fs_type; + const char* alias; + int deprecated; +}; + + +/** + * Structure describing file system + */ +struct _PedFileSystem { + PedFileSystemType* type; /**< the file system type */ + PedGeometry* geom; /**< where the file system actually is */ + int checked; /**< 1 if the file system has been checked. + 0 otherwise. */ + + void* type_specific; + +}; + +extern void ped_file_system_type_register (PedFileSystemType* type); +extern void ped_file_system_type_unregister (PedFileSystemType* type); + +extern void ped_file_system_alias_register (PedFileSystemType* type, + const char* alias, int deprecated); +extern void ped_file_system_alias_unregister (PedFileSystemType* type, + const char* alias); + +extern PedFileSystemType* ped_file_system_type_get (const char* name); +extern PedFileSystemType* +ped_file_system_type_get_next (const PedFileSystemType* fs_type); + +extern PedFileSystemAlias* +ped_file_system_alias_get_next (const PedFileSystemAlias* fs_alias); + +extern PedFileSystemType* ped_file_system_probe (PedGeometry* geom); +extern PedGeometry* ped_file_system_probe_specific ( + const PedFileSystemType* fs_type, + PedGeometry* geom); +extern int ped_file_system_clobber (PedGeometry* geom); + +extern PedFileSystem* ped_file_system_open (PedGeometry* geom); +extern PedFileSystem* ped_file_system_create (PedGeometry* geom, + const PedFileSystemType* type, + PedTimer* timer); +extern int ped_file_system_close (PedFileSystem* fs); +extern int ped_file_system_check (PedFileSystem* fs, PedTimer* timer); +extern PedFileSystem* ped_file_system_copy (PedFileSystem* fs, + PedGeometry* geom, + PedTimer* timer); +extern int ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, + PedTimer* timer); + +extern PedConstraint* ped_file_system_get_create_constraint ( + const PedFileSystemType* fs_type, const PedDevice* dev); +extern PedConstraint* ped_file_system_get_resize_constraint ( + const PedFileSystem* fs); +extern PedConstraint* ped_file_system_get_copy_constraint ( + const PedFileSystem* fs, const PedDevice* dev); + +#endif /* PED_FILESYS_H_INCLUDED */ + +/** @} */ diff --git a/include/parted/geom.h b/include/parted/geom.h new file mode 100644 index 0000000..0aea2b5 --- /dev/null +++ b/include/parted/geom.h @@ -0,0 +1,85 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1998-2001, 2005, 2007, 2009-2011 Free Software Foundation, + Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedGeometry + * @{ + */ + +/** \file geom.h */ + +#ifndef PED_GEOM_H_INCLUDED +#define PED_GEOM_H_INCLUDED + +typedef struct _PedGeometry PedGeometry; + +/** + * Geometry of the partition + */ +struct _PedGeometry { + PedDevice* dev; + PedSector start; + PedSector length; + PedSector end; +}; + +#include <parted/device.h> +#include <parted/timer.h> + +extern int ped_geometry_init (PedGeometry* geom, const PedDevice* dev, + PedSector start, PedSector length); +extern PedGeometry* ped_geometry_new (const PedDevice* dev, PedSector start, + PedSector length); +extern PedGeometry* ped_geometry_duplicate (const PedGeometry* geom); +extern PedGeometry* ped_geometry_intersect (const PedGeometry* a, + const PedGeometry* b); +extern void ped_geometry_destroy (PedGeometry* geom); +extern int ped_geometry_set (PedGeometry* geom, PedSector start, + PedSector length); +extern int ped_geometry_set_start (PedGeometry* geom, PedSector start); +extern int ped_geometry_set_end (PedGeometry* geom, PedSector end); +extern int ped_geometry_test_overlap (const PedGeometry* a, + const PedGeometry* b); +extern int ped_geometry_test_inside (const PedGeometry* a, + const PedGeometry* b); +extern int ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b); +extern int ped_geometry_test_sector_inside (const PedGeometry* geom, + PedSector sect); + +extern int ped_geometry_read (const PedGeometry* geom, void* buffer, + PedSector offset, PedSector count); +extern int ped_geometry_read_alloc (const PedGeometry* geom, void** buffer, + PedSector offset, PedSector count); +extern int ped_geometry_write (PedGeometry* geom, const void* buffer, + PedSector offset, PedSector count); +extern PedSector ped_geometry_check (PedGeometry* geom, void* buffer, + PedSector buffer_size, PedSector offset, + PedSector granularity, PedSector count, + PedTimer* timer); +extern int ped_geometry_sync (PedGeometry* geom); +extern int ped_geometry_sync_fast (PedGeometry* geom); + +/* returns -1 if "sector" is not within dest's space. */ +extern PedSector ped_geometry_map (const PedGeometry* dst, + const PedGeometry* src, + PedSector sector); + +#endif /* PED_GEOM_H_INCLUDED */ + +/** @} */ diff --git a/include/parted/natmath.h b/include/parted/natmath.h new file mode 100644 index 0000000..02c5ee8 --- /dev/null +++ b/include/parted/natmath.h @@ -0,0 +1,108 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedAlignment + * @{ + */ + +/** \file natmath.h */ + +#ifndef PED_NATMATH_H_INCLUDED +#define PED_NATMATH_H_INCLUDED + + +typedef struct _PedAlignment PedAlignment; + +#include <parted/disk.h> +#include <parted/device.h> +#include <parted/geom.h> + +#define PED_MIN(a, b) ( ((a)<(b)) ? (a) : (b) ) +#define PED_MAX(a, b) ( ((a)>(b)) ? (a) : (b) ) + +/* this is weird (I'm still not sure I should be doing this!) + * + * For the functions: new, destroy, duplicate and merge: the following values + * for align are valid: + * * align == NULL (!) represents no solution + * * align->grain_size == 0 represents a single solution + * (align->offset) + * * align->grain_size > 0 represents a set of solutions + * + * These are invalid: + * * align->offset < 0 Note: this gets "normalized" + * * align->grain_size < 0 + * + * For the align_* operations, there must be a solution. i.e. align != NULL + * All solutions must be greater than zero. + */ + +struct _PedAlignment { + PedSector offset; + PedSector grain_size; +}; + +extern PedSector ped_round_up_to (PedSector sector, PedSector grain_size); +extern PedSector ped_round_down_to (PedSector sector, PedSector grain_size); +extern PedSector ped_round_to_nearest (PedSector sector, PedSector grain_size); +extern PedSector ped_greatest_common_divisor (PedSector a, PedSector b); + +extern int ped_alignment_init (PedAlignment* align, PedSector offset, + PedSector grain_size); +extern PedAlignment* ped_alignment_new (PedSector offset, PedSector grain_size); +extern void ped_alignment_destroy (PedAlignment* align); +extern PedAlignment* ped_alignment_duplicate (const PedAlignment* align); +extern PedAlignment* ped_alignment_intersect (const PedAlignment* a, + const PedAlignment* b); + +extern PedSector +ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom, + PedSector sector); +extern PedSector +ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom, + PedSector sector); +extern PedSector +ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom, + PedSector sector); + +extern int +ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom, + PedSector sector); + +extern const PedAlignment* ped_alignment_any; +extern const PedAlignment* ped_alignment_none; + +static inline PedSector +ped_div_round_up (PedSector numerator, PedSector divisor) +{ + return (numerator + divisor - 1) / divisor; +} + + +static inline PedSector +ped_div_round_to_nearest (PedSector numerator, PedSector divisor) +{ + return (numerator + divisor/2) / divisor; +} + +#endif /* PED_NATMATH_H_INCLUDED */ + +/** + * @} + */ diff --git a/include/parted/parted.h b/include/parted/parted.h new file mode 100644 index 0000000..f0e8847 --- /dev/null +++ b/include/parted/parted.h @@ -0,0 +1,51 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2001, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PARTED_H_INCLUDED +#define PARTED_H_INCLUDED + +#define PED_DEFAULT_ALIGNMENT (1024 * 1024) + +#ifdef __cplusplus +extern "C" { +#endif + +#include <parted/constraint.h> +#include <parted/device.h> +#include <parted/disk.h> +#include <parted/exception.h> +#include <parted/filesys.h> +#include <parted/natmath.h> +#include <parted/unit.h> + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +extern const char* ped_get_version (); + +extern void* ped_malloc (size_t size); +extern void* ped_calloc (size_t size); +extern int ped_realloc (void** ptr, size_t size); +extern void free (void* ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* PARTED_H_INCLUDED */ diff --git a/include/parted/timer.h b/include/parted/timer.h new file mode 100644 index 0000000..de3cef5 --- /dev/null +++ b/include/parted/timer.h @@ -0,0 +1,65 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001-2002, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedTimer + * @{ + */ + +/** \file timer.h */ + +#ifndef PED_TIMER_H_INCLUDED +#define PED_TIMER_H_INCLUDED + +#include <time.h> + +typedef struct _PedTimer PedTimer; + +typedef void PedTimerHandler (PedTimer* timer, void* context); + +/* + * Structure keeping track of progress and time + */ +struct _PedTimer { + float frac; /**< fraction of operation done */ + time_t start; /**< time of start of op */ + time_t now; /**< time of last update (now!) */ + time_t predicted_end; /**< expected finish time */ + const char* state_name; /**< eg: "copying data" */ + PedTimerHandler* handler; /**< who to notify on updates */ + void* context; /**< context to pass to handler */ +}; + +extern PedTimer* ped_timer_new (PedTimerHandler* handler, void* context); +extern void ped_timer_destroy (PedTimer* timer); + +/* a nested timer automatically notifies it's parent. You should only + * create one when you are going to use it (not before) + */ +extern PedTimer* ped_timer_new_nested (PedTimer* parent, float nest_frac); +extern void ped_timer_destroy_nested (PedTimer* timer); + +extern void ped_timer_touch (PedTimer* timer); +extern void ped_timer_reset (PedTimer* timer); +extern void ped_timer_update (PedTimer* timer, float new_frac); +extern void ped_timer_set_state_name (PedTimer* timer, const char* state_name); + +#endif /* PED_TIMER_H_INCLUDED */ + + +/** @} */ diff --git a/include/parted/unit.h b/include/parted/unit.h new file mode 100644 index 0000000..ceb53ac --- /dev/null +++ b/include/parted/unit.h @@ -0,0 +1,91 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedUnit + * @{ + */ + +/** \file unit.h */ + +#ifndef PED_UNIT_H_INCLUDED +#define PED_UNIT_H_INCLUDED + +#include <parted/device.h> + +#include <stdarg.h> +#include <stdio.h> + +#define PED_SECTOR_SIZE_DEFAULT 512LL +#define PED_KILOBYTE_SIZE 1000LL +#define PED_MEGABYTE_SIZE 1000000LL +#define PED_GIGABYTE_SIZE 1000000000LL +#define PED_TERABYTE_SIZE 1000000000000LL +#define PED_KIBIBYTE_SIZE 1024LL +#define PED_MEBIBYTE_SIZE 1048576LL +#define PED_GIBIBYTE_SIZE 1073741824LL +#define PED_TEBIBYTE_SIZE 1099511627776LL + +/** + * Human-friendly unit for representation of a location within device + */ +typedef enum { + PED_UNIT_SECTOR, + PED_UNIT_BYTE, + PED_UNIT_KILOBYTE, + PED_UNIT_MEGABYTE, + PED_UNIT_GIGABYTE, + PED_UNIT_TERABYTE, + PED_UNIT_COMPACT, + PED_UNIT_CYLINDER, + PED_UNIT_CHS, + PED_UNIT_PERCENT, + PED_UNIT_KIBIBYTE, + PED_UNIT_MEBIBYTE, + PED_UNIT_GIBIBYTE, + PED_UNIT_TEBIBYTE +} PedUnit; + +#define PED_UNIT_FIRST PED_UNIT_SECTOR +#define PED_UNIT_LAST PED_UNIT_TEBIBYTE + +extern long long ped_unit_get_size (const PedDevice* dev, PedUnit unit); +extern const char* ped_unit_get_name (PedUnit unit); +extern PedUnit ped_unit_get_by_name (const char* unit_name); + +extern void ped_unit_set_default (PedUnit unit); +extern PedUnit ped_unit_get_default (); + +extern char* ped_unit_format_byte (const PedDevice* dev, PedSector byte); +extern char* ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, + PedUnit unit); + +extern char* ped_unit_format (const PedDevice* dev, PedSector sector); +extern char* ped_unit_format_custom (const PedDevice* dev, PedSector sector, + PedUnit unit); + +extern int ped_unit_parse (const char* str, const PedDevice* dev, + PedSector* sector, + PedGeometry** range); +extern int ped_unit_parse_custom (const char* str, const PedDevice* dev, + PedUnit unit, PedSector* sector, + PedGeometry** range); + +#endif /* PED_UNIT_H_INCLUDED */ + +/** @} */ diff --git a/include/progname.h b/include/progname.h new file mode 100644 index 0000000..0844066 --- /dev/null +++ b/include/progname.h @@ -0,0 +1,62 @@ +/* Program name management. + Copyright (C) 2001-2004, 2006, 2009-2011 Free Software Foundation, Inc. + Written by Bruno Haible <bruno@clisp.org>, 2001. + + 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 <http://www.gnu.org/licenses/>. */ + +#ifndef _PROGNAME_H +#define _PROGNAME_H + +/* Programs using this file should do the following in main(): + set_program_name (argv[0]); + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* String containing name the program is called with. */ +extern const char *program_name; + +/* Set program_name, based on argv[0]. + argv0 must be a string allocated with indefinite extent, and must not be + modified after this call. */ +extern void set_program_name (const char *argv0); + +#if ENABLE_RELOCATABLE + +/* Set program_name, based on argv[0], and original installation prefix and + directory, for relocatability. */ +extern void set_program_name_and_installdir (const char *argv0, + const char *orig_installprefix, + const char *orig_installdir); +#undef set_program_name +#define set_program_name(ARG0) \ + set_program_name_and_installdir (ARG0, INSTALLPREFIX, INSTALLDIR) + +/* Return the full pathname of the current executable, based on the earlier + call to set_program_name_and_installdir. Return NULL if unknown. */ +extern char *get_full_program_name (void); + +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif /* _PROGNAME_H */ diff --git a/include/verify.h b/include/verify.h new file mode 100644 index 0000000..3294b30 --- /dev/null +++ b/include/verify.h @@ -0,0 +1,232 @@ +/* Compile-time assert-like macros. + + Copyright (C) 2005-2006, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */ + +#ifndef _GL_VERIFY_H +# define _GL_VERIFY_H + + +/* Define _GL_HAVE__STATIC_ASSERT to 1 if _Static_assert works as per the + C1X draft N1548 section 6.7.10. This is supported by GCC 4.6.0 and + later, in C mode, and its use here generates easier-to-read diagnostics + when verify (R) fails. + + Define _GL_HAVE_STATIC_ASSERT to 1 if static_assert works as per the + C++0X draft N3242 section 7.(4). + This will likely be supported by future GCC versions, in C++ mode. + + Use this only with GCC. If we were willing to slow 'configure' + down we could also use it with other compilers, but since this + affects only the quality of diagnostics, why bother? */ +# if (4 < __GNUC__ || (__GNUC__ == 4 && 6 <= __GNUC_MINOR__)) && !defined __cplusplus +# define _GL_HAVE__STATIC_ASSERT 1 +# endif +/* The condition (99 < __GNUC__) is temporary, until we know about the + first G++ release that supports static_assert. */ +# if (99 < __GNUC__) && defined __cplusplus +# define _GL_HAVE_STATIC_ASSERT 1 +# endif + +/* Each of these macros verifies that its argument R is nonzero. To + be portable, R should be an integer constant expression. Unlike + assert (R), there is no run-time overhead. + + If _Static_assert works, verify (R) uses it directly. Similarly, + _GL_VERIFY_TRUE works by packaging a _Static_assert inside a struct + that is an operand of sizeof. + + The code below uses several ideas for C++ compilers, and for C + compilers that do not support _Static_assert: + + * The first step is ((R) ? 1 : -1). Given an expression R, of + integral or boolean or floating-point type, this yields an + expression of integral type, whose value is later verified to be + constant and nonnegative. + + * Next this expression W is wrapped in a type + struct _gl_verify_type { + unsigned int _gl_verify_error_if_negative: W; + }. + If W is negative, this yields a compile-time error. No compiler can + deal with a bit-field of negative size. + + One might think that an array size check would have the same + effect, that is, that the type struct { unsigned int dummy[W]; } + would work as well. However, inside a function, some compilers + (such as C++ compilers and GNU C) allow local parameters and + variables inside array size expressions. With these compilers, + an array size check would not properly diagnose this misuse of + the verify macro: + + void function (int n) { verify (n < 0); } + + * For the verify macro, the struct _gl_verify_type will need to + somehow be embedded into a declaration. To be portable, this + declaration must declare an object, a constant, a function, or a + typedef name. If the declared entity uses the type directly, + such as in + + struct dummy {...}; + typedef struct {...} dummy; + extern struct {...} *dummy; + extern void dummy (struct {...} *); + extern struct {...} *dummy (void); + + two uses of the verify macro would yield colliding declarations + if the entity names are not disambiguated. A workaround is to + attach the current line number to the entity name: + + #define _GL_CONCAT0(x, y) x##y + #define _GL_CONCAT(x, y) _GL_CONCAT0 (x, y) + extern struct {...} * _GL_CONCAT (dummy, __LINE__); + + But this has the problem that two invocations of verify from + within the same macro would collide, since the __LINE__ value + would be the same for both invocations. (The GCC __COUNTER__ + macro solves this problem, but is not portable.) + + A solution is to use the sizeof operator. It yields a number, + getting rid of the identity of the type. Declarations like + + extern int dummy [sizeof (struct {...})]; + extern void dummy (int [sizeof (struct {...})]); + extern int (*dummy (void)) [sizeof (struct {...})]; + + can be repeated. + + * Should the implementation use a named struct or an unnamed struct? + Which of the following alternatives can be used? + + extern int dummy [sizeof (struct {...})]; + extern int dummy [sizeof (struct _gl_verify_type {...})]; + extern void dummy (int [sizeof (struct {...})]); + extern void dummy (int [sizeof (struct _gl_verify_type {...})]); + extern int (*dummy (void)) [sizeof (struct {...})]; + extern int (*dummy (void)) [sizeof (struct _gl_verify_type {...})]; + + In the second and sixth case, the struct type is exported to the + outer scope; two such declarations therefore collide. GCC warns + about the first, third, and fourth cases. So the only remaining + possibility is the fifth case: + + extern int (*dummy (void)) [sizeof (struct {...})]; + + * GCC warns about duplicate declarations of the dummy function if + -Wredundant_decls is used. GCC 4.3 and later have a builtin + __COUNTER__ macro that can let us generate unique identifiers for + each dummy function, to suppress this warning. + + * This implementation exploits the fact that older versions of GCC, + which do not support _Static_assert, also do not warn about the + last declaration mentioned above. + + * In C++, any struct definition inside sizeof is invalid. + Use a template type to work around the problem. */ + +/* Concatenate two preprocessor tokens. */ +# define _GL_CONCAT(x, y) _GL_CONCAT0 (x, y) +# define _GL_CONCAT0(x, y) x##y + +/* _GL_COUNTER is an integer, preferably one that changes each time we + use it. Use __COUNTER__ if it works, falling back on __LINE__ + otherwise. __LINE__ isn't perfect, but it's better than a + constant. */ +# if defined __COUNTER__ && __COUNTER__ != __COUNTER__ +# define _GL_COUNTER __COUNTER__ +# else +# define _GL_COUNTER __LINE__ +# endif + +/* Generate a symbol with the given prefix, making it unique if + possible. */ +# define _GL_GENSYM(prefix) _GL_CONCAT (prefix, _GL_COUNTER) + +/* Verify requirement R at compile-time, as an integer constant expression + that returns 1. If R is false, fail at compile-time, preferably + with a diagnostic that includes the string-literal DIAGNOSTIC. */ + +# define _GL_VERIFY_TRUE(R, DIAGNOSTIC) \ + (!!sizeof (_GL_VERIFY_TYPE (R, DIAGNOSTIC))) + +# ifdef __cplusplus +template <int w> + struct _gl_verify_type { + unsigned int _gl_verify_error_if_negative: w; + }; +# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \ + _gl_verify_type<(R) ? 1 : -1> +# elif defined _GL_HAVE__STATIC_ASSERT +# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \ + struct { \ + _Static_assert (R, DIAGNOSTIC); \ + int _gl_dummy; \ + } +# else +# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \ + struct { unsigned int _gl_verify_error_if_negative: (R) ? 1 : -1; } +# endif + +/* Verify requirement R at compile-time, as a declaration without a + trailing ';'. If R is false, fail at compile-time, preferably + with a diagnostic that includes the string-literal DIAGNOSTIC. + + Unfortunately, unlike C1X, this implementation must appear as an + ordinary declaration, and cannot appear inside struct { ... }. */ + +# ifdef _GL_HAVE__STATIC_ASSERT +# define _GL_VERIFY _Static_assert +# else +# define _GL_VERIFY(R, DIAGNOSTIC) \ + extern int (*_GL_GENSYM (_gl_verify_function) (void)) \ + [_GL_VERIFY_TRUE (R, DIAGNOSTIC)] +# endif + +/* _GL_STATIC_ASSERT_H is defined if this code is copied into assert.h. */ +# ifdef _GL_STATIC_ASSERT_H +# if !defined _GL_HAVE__STATIC_ASSERT && !defined _Static_assert +# define _Static_assert(R, DIAGNOSTIC) _GL_VERIFY (R, DIAGNOSTIC) +# endif +# if !defined _GL_HAVE_STATIC_ASSERT && !defined static_assert +# define static_assert _Static_assert /* Draft C1X requires this #define. */ +# endif +# endif + +# ifdef _GL_VERIFY_H + +/* Each of these macros verifies that its argument R is nonzero. To + be portable, R should be an integer constant expression. Unlike + assert (R), there is no run-time overhead. + + There are two macros, since no single macro can be used in all + contexts in C. verify_true (R) is for scalar contexts, including + integer constant expression contexts. verify (R) is for declaration + contexts, e.g., the top level. */ + +/* Verify requirement R at compile-time, as an integer constant expression. + Return 1. */ + +# define verify_true(R) _GL_VERIFY_TRUE (R, "verify_true (" #R ")") + +/* Verify requirement R at compile-time, as a declaration without a + trailing ';'. */ + +# define verify(R) _GL_VERIFY (R, "verify (" #R ")") + +# endif + +#endif diff --git a/include/version-etc.h b/include/version-etc.h new file mode 100644 index 0000000..a9b313d --- /dev/null +++ b/include/version-etc.h @@ -0,0 +1,78 @@ +/* Print --version and bug-reporting information in a consistent format. + Copyright (C) 1999, 2003, 2005, 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Jim Meyering. */ + +#ifndef VERSION_ETC_H +# define VERSION_ETC_H 1 + +# include <stdarg.h> +# include <stdio.h> + +/* The `sentinel' attribute was added in gcc 4.0. */ +#ifndef _GL_ATTRIBUTE_SENTINEL +# if 4 <= __GNUC__ +# define _GL_ATTRIBUTE_SENTINEL __attribute__ ((__sentinel__)) +# else +# define _GL_ATTRIBUTE_SENTINEL /* empty */ +# endif +#endif + +extern const char version_etc_copyright[]; + +/* The three functions below display the --version information in the + standard way: command and package names, package version, followed + by a short GPLv3+ notice and a list of up to 10 author names. + + If COMMAND_NAME is NULL, the PACKAGE is asumed to be the name of + the program. The formats are therefore: + + PACKAGE VERSION + + or + + COMMAND_NAME (PACKAGE) VERSION. + + The functions differ in the way they are passed author names: */ + +/* N_AUTHORS names are supplied in array AUTHORS. */ +extern void version_etc_arn (FILE *stream, + const char *command_name, const char *package, + const char *version, + const char * const * authors, size_t n_authors); + +/* Names are passed in the NULL-terminated array AUTHORS. */ +extern void version_etc_ar (FILE *stream, + const char *command_name, const char *package, + const char *version, const char * const * authors); + +/* Names are passed in the NULL-terminated va_list. */ +extern void version_etc_va (FILE *stream, + const char *command_name, const char *package, + const char *version, va_list authors); + +/* Names are passed as separate arguments, with an additional + NULL argument at the end. */ +extern void version_etc (FILE *stream, + const char *command_name, const char *package, + const char *version, + /* const char *author1, ..., NULL */ ...) + _GL_ATTRIBUTE_SENTINEL; + +/* Display the usual `Report bugs to' stanza */ +extern void emit_bug_reporting_address (void); + +#endif /* VERSION_ETC_H */ diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/include/version.h diff --git a/include/xalloc-oversized.h b/include/xalloc-oversized.h new file mode 100644 index 0000000..ab19bcf --- /dev/null +++ b/include/xalloc-oversized.h @@ -0,0 +1,38 @@ +/* xalloc-oversized.h -- memory allocation size checking + + Copyright (C) 1990-2000, 2003-2004, 2006-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef XALLOC_OVERSIZED_H_ +# define XALLOC_OVERSIZED_H_ + +# include <stddef.h> + +/* Return 1 if an array of N objects, each of size S, cannot exist due + to size arithmetic overflow. S must be positive and N must be + nonnegative. This is a macro, not an inline function, so that it + works correctly even when SIZE_MAX < N. + + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative dividend to use here is + SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. + However, malloc (SIZE_MAX) fails on all known hosts where + sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for + exactly-SIZE_MAX allocations on such hosts; this avoids a test and + branch when S is known to be 1. */ +# define xalloc_oversized(n, s) \ + ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) + +#endif /* !XALLOC_OVERSIZED_H_ */ diff --git a/include/xalloc.h b/include/xalloc.h new file mode 100644 index 0000000..c1bbe7e --- /dev/null +++ b/include/xalloc.h @@ -0,0 +1,277 @@ +/* xalloc.h -- malloc with out-of-memory checking + + Copyright (C) 1990-2000, 2003-2004, 2006-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef XALLOC_H_ +# define XALLOC_H_ + +# include <stddef.h> + +# include "xalloc-oversized.h" + +# ifdef __cplusplus +extern "C" { +# endif + + +# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +# define _GL_ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +# else +# define _GL_ATTRIBUTE_NORETURN /* empty */ +# endif + +# if __GNUC__ >= 3 +# define _GL_ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) +# else +# define _GL_ATTRIBUTE_MALLOC +# endif + +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) +# define _GL_ATTRIBUTE_ALLOC_SIZE(args) __attribute__ ((__alloc_size__ args)) +# else +# define _GL_ATTRIBUTE_ALLOC_SIZE(args) +# endif + +/* This function is always triggered when memory is exhausted. + It must be defined by the application, either explicitly + or by using gnulib's xalloc-die module. This is the + function to call when one wants the program to die because of a + memory allocation failure. */ +extern void xalloc_die (void) _GL_ATTRIBUTE_NORETURN; + +void *xmalloc (size_t s) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1)); +void *xzalloc (size_t s) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1)); +void *xcalloc (size_t n, size_t s) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1, 2)); +void *xrealloc (void *p, size_t s) + _GL_ATTRIBUTE_ALLOC_SIZE ((2)); +void *x2realloc (void *p, size_t *pn); +void *xmemdup (void const *p, size_t s) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((2)); +char *xstrdup (char const *str) + _GL_ATTRIBUTE_MALLOC; + +/* In the following macros, T must be an elementary or structure/union or + typedef'ed type, or a pointer to such a type. To apply one of the + following macros to a function pointer or array type, you need to typedef + it first and use the typedef name. */ + +/* Allocate an object of type T dynamically, with error checking. */ +/* extern t *XMALLOC (typename t); */ +# define XMALLOC(t) ((t *) xmalloc (sizeof (t))) + +/* Allocate memory for N elements of type T, with error checking. */ +/* extern t *XNMALLOC (size_t n, typename t); */ +# define XNMALLOC(n, t) \ + ((t *) (sizeof (t) == 1 ? xmalloc (n) : xnmalloc (n, sizeof (t)))) + +/* Allocate an object of type T dynamically, with error checking, + and zero it. */ +/* extern t *XZALLOC (typename t); */ +# define XZALLOC(t) ((t *) xzalloc (sizeof (t))) + +/* Allocate memory for N elements of type T, with error checking, + and zero it. */ +/* extern t *XCALLOC (size_t n, typename t); */ +# define XCALLOC(n, t) \ + ((t *) (sizeof (t) == 1 ? xzalloc (n) : xcalloc (n, sizeof (t)))) + + +# if HAVE_INLINE +# define static_inline static inline +# else +void *xnmalloc (size_t n, size_t s) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1, 2)); +void *xnrealloc (void *p, size_t n, size_t s) + _GL_ATTRIBUTE_ALLOC_SIZE ((2, 3)); +void *x2nrealloc (void *p, size_t *pn, size_t s); +char *xcharalloc (size_t n) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1)); +# endif + +# ifdef static_inline + +/* Allocate an array of N objects, each with S bytes of memory, + dynamically, with error checking. S must be nonzero. */ + +static_inline void *xnmalloc (size_t n, size_t s) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1, 2)); +static_inline void * +xnmalloc (size_t n, size_t s) +{ + if (xalloc_oversized (n, s)) + xalloc_die (); + return xmalloc (n * s); +} + +/* Change the size of an allocated block of memory P to an array of N + objects each of S bytes, with error checking. S must be nonzero. */ + +static_inline void *xnrealloc (void *p, size_t n, size_t s) + _GL_ATTRIBUTE_ALLOC_SIZE ((2, 3)); +static_inline void * +xnrealloc (void *p, size_t n, size_t s) +{ + if (xalloc_oversized (n, s)) + xalloc_die (); + return xrealloc (p, n * s); +} + +/* If P is null, allocate a block of at least *PN such objects; + otherwise, reallocate P so that it contains more than *PN objects + each of S bytes. *PN must be nonzero unless P is null, and S must + be nonzero. Set *PN to the new number of objects, and return the + pointer to the new block. *PN is never set to zero, and the + returned pointer is never null. + + Repeated reallocations are guaranteed to make progress, either by + allocating an initial block with a nonzero size, or by allocating a + larger block. + + In the following implementation, nonzero sizes are increased by a + factor of approximately 1.5 so that repeated reallocations have + O(N) overall cost rather than O(N**2) cost, but the + specification for this function does not guarantee that rate. + + Here is an example of use: + + int *p = NULL; + size_t used = 0; + size_t allocated = 0; + + void + append_int (int value) + { + if (used == allocated) + p = x2nrealloc (p, &allocated, sizeof *p); + p[used++] = value; + } + + This causes x2nrealloc to allocate a block of some nonzero size the + first time it is called. + + To have finer-grained control over the initial size, set *PN to a + nonzero value before calling this function with P == NULL. For + example: + + int *p = NULL; + size_t used = 0; + size_t allocated = 0; + size_t allocated1 = 1000; + + void + append_int (int value) + { + if (used == allocated) + { + p = x2nrealloc (p, &allocated1, sizeof *p); + allocated = allocated1; + } + p[used++] = value; + } + + */ + +static_inline void * +x2nrealloc (void *p, size_t *pn, size_t s) +{ + size_t n = *pn; + + if (! p) + { + if (! n) + { + /* The approximate size to use for initial small allocation + requests, when the invoking code specifies an old size of + zero. 64 bytes is the largest "small" request for the + GNU C library malloc. */ + enum { DEFAULT_MXFAST = 64 }; + + n = DEFAULT_MXFAST / s; + n += !n; + } + } + else + { + /* Set N = ceil (1.5 * N) so that progress is made if N == 1. + Check for overflow, so that N * S stays in size_t range. + The check is slightly conservative, but an exact check isn't + worth the trouble. */ + if ((size_t) -1 / 3 * 2 / s <= n) + xalloc_die (); + n += (n + 1) / 2; + } + + *pn = n; + return xrealloc (p, n * s); +} + +/* Return a pointer to a new buffer of N bytes. This is like xmalloc, + except it returns char *. */ + +static_inline char *xcharalloc (size_t n) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_ALLOC_SIZE ((1)); +static_inline char * +xcharalloc (size_t n) +{ + return XNMALLOC (n, char); +} + +# endif + +# ifdef __cplusplus +} + +/* C++ does not allow conversions from void * to other pointer types + without a cast. Use templates to work around the problem when + possible. */ + +template <typename T> inline T * +xrealloc (T *p, size_t s) +{ + return (T *) xrealloc ((void *) p, s); +} + +template <typename T> inline T * +xnrealloc (T *p, size_t n, size_t s) +{ + return (T *) xnrealloc ((void *) p, n, s); +} + +template <typename T> inline T * +x2realloc (T *p, size_t *pn) +{ + return (T *) x2realloc ((void *) p, pn); +} + +template <typename T> inline T * +x2nrealloc (T *p, size_t *pn, size_t s) +{ + return (T *) x2nrealloc ((void *) p, pn, s); +} + +template <typename T> inline T * +xmemdup (T const *p, size_t s) +{ + return (T *) xmemdup ((void const *) p, s); +} + +# endif + + +#endif /* !XALLOC_H_ */ diff --git a/include/xstrtol.h b/include/xstrtol.h new file mode 100644 index 0000000..716a94f --- /dev/null +++ b/include/xstrtol.h @@ -0,0 +1,79 @@ +/* A more useful interface to strtol. + + Copyright (C) 1995-1996, 1998-1999, 2001-2004, 2006-2011 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef XSTRTOL_H_ +# define XSTRTOL_H_ 1 + +# include <getopt.h> +# include <inttypes.h> + +# ifndef _STRTOL_ERROR +enum strtol_error + { + LONGINT_OK = 0, + + /* These two values can be ORed together, to indicate that both + errors occurred. */ + LONGINT_OVERFLOW = 1, + LONGINT_INVALID_SUFFIX_CHAR = 2, + + LONGINT_INVALID_SUFFIX_CHAR_WITH_OVERFLOW = (LONGINT_INVALID_SUFFIX_CHAR + | LONGINT_OVERFLOW), + LONGINT_INVALID = 4 + }; +typedef enum strtol_error strtol_error; +# endif + +# define _DECLARE_XSTRTOL(name, type) \ + strtol_error name (const char *, char **, int, type *, const char *); +_DECLARE_XSTRTOL (xstrtol, long int) +_DECLARE_XSTRTOL (xstrtoul, unsigned long int) +_DECLARE_XSTRTOL (xstrtoimax, intmax_t) +_DECLARE_XSTRTOL (xstrtoumax, uintmax_t) + +#if HAVE_LONG_LONG_INT +_DECLARE_XSTRTOL (xstrtoll, long long int) +_DECLARE_XSTRTOL (xstrtoull, unsigned long long int) +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +# define _GL_ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +#else +# define _GL_ATTRIBUTE_NORETURN /* empty */ +#endif + +/* Report an error for an invalid integer in an option argument. + + ERR is the error code returned by one of the xstrto* functions. + + Use OPT_IDX to decide whether to print the short option string "C" + or "-C" or a long option string derived from LONG_OPTION. OPT_IDX + is -2 if the short option "C" was used, without any leading "-"; it + is -1 if the short option "-C" was used; otherwise it is an index + into LONG_OPTIONS, which should have a name preceded by two '-' + characters. + + ARG is the option-argument containing the integer. + + After reporting an error, exit with a failure status. */ + +void xstrtol_fatal (enum strtol_error, + int, char, struct option const *, + char const *) _GL_ATTRIBUTE_NORETURN; + +#endif /* not XSTRTOL_H_ */ diff --git a/libparted/Makefile b/libparted/Makefile new file mode 100644 index 0000000..ae004a5 --- /dev/null +++ b/libparted/Makefile @@ -0,0 +1,21 @@ + +CFLAGS+= -I../include -Wall -O2 + +OBJS= exception.o unit.o libparted.o debug.o device.o timer.o disk.o architecture.o \ + filesys.o linux.o + +OBJS+= cs/constraint.o cs/geom.o cs/natmath.o + +OBJS+= fs/hfs/advfs.o fs/hfs/cache.o fs/hfs/file_plus.o fs/hfs/journal.o \ + fs/hfs/reloc.o fs/hfs/advfs_plus.o fs/hfs/file.o fs/hfs/hfs.o \ + fs/hfs/probe.o fs/hfs/reloc_plus.o + +OBJS+= fs/fat/bootsector.o fs/fat/calc.o fs/fat/clstdup.o fs/fat/context.o \ + fs/fat/count.o fs/fat/fat.o fs/fat/fatio.o fs/fat/resize.o \ + fs/fat/table.o fs/fat/traverse.o + +libparted.a: $(OBJS) + $(LD) -r -o libparted.a $(OBJS) + +clean: + rm -f $(OBJS) libparted.a diff --git a/libparted/architecture.c b/libparted/architecture.c new file mode 100644 index 0000000..7af13d5 --- /dev/null +++ b/libparted/architecture.c @@ -0,0 +1,43 @@ + /* + libparted - a library for manipulating disk partitions + Copyright (C) 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "architecture.h" + +const PedArchitecture* ped_architecture; + +void +ped_set_architecture () +{ + /* Set just once */ + if (ped_architecture) + return; + +#ifdef linux + extern PedArchitecture ped_linux_arch; + const PedArchitecture* arch = &ped_linux_arch; +#elif defined(__BEOS__) + extern PedArchitecture ped_beos_arch; + const PedArchitecture* arch = &ped_beos_arch; +#else + extern PedArchitecture ped_gnu_arch; + const PedArchitecture* arch = &ped_gnu_arch; +#endif + + ped_architecture = arch; +} diff --git a/libparted/architecture.h b/libparted/architecture.h new file mode 100644 index 0000000..eee6c04 --- /dev/null +++ b/libparted/architecture.h @@ -0,0 +1,38 @@ + /* + libparted - a library for manipulating disk partitions + Copyright (C) 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * WARNING: This shouldn't be exported to the API + */ + +#ifndef _LIBPARTED_ARCH_H_INCLUDED +#define _LIBPARTED_ARCH_H_INCLUDED + +#include <parted/disk.h> + +struct _PedArchitecture { + PedDiskArchOps* disk_ops; + PedDeviceArchOps* dev_ops; +}; +typedef struct _PedArchitecture PedArchitecture; + +extern const PedArchitecture* ped_architecture; + +extern void ped_set_architecture (); + +#endif /* _LIBPARTED_ARCH_H_INCLUDED */ diff --git a/libparted/cs/constraint.c b/libparted/cs/constraint.c new file mode 100644 index 0000000..f4181c5 --- /dev/null +++ b/libparted/cs/constraint.c @@ -0,0 +1,527 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \addtogroup PedConstraint + * + * \brief Constraint solver interface. + * + * Constraints are used to communicate restrictions on operations Constraints + * are restrictions on the location and alignment of the start and end of a + * partition, and the minimum and maximum size. + * + * Constraints are closed under intersection (for the proof see the source + * code). For background information see the Chinese Remainder Theorem. + * + * This interface consists of construction constraints, finding the intersection + * of constraints, and finding solutions to constraints. + * + * The constraint solver allows you to specify constraints on where a partition + * or file system (or any PedGeometry) may be placed/resized/etc. For example, + * you might want to make sure that a file system is at least 10 Gb, or that it + * starts at the beginning of new cylinder. + * + * The constraint solver in this file unifies solver in geom.c (which allows you + * to specify constraints on ranges) and natmath.c (which allows you to specify + * alignment constraints). + * + * @{ + */ + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> + +/** + * Initializes a pre-allocated piece of memory to contain a constraint + * with the supplied default values. + * + * \return \c 0 on failure. + */ +int +ped_constraint_init ( + PedConstraint* constraint, + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size) +{ + PED_ASSERT (constraint != NULL); + PED_ASSERT (start_range != NULL); + PED_ASSERT (end_range != NULL); + PED_ASSERT (min_size > 0); + PED_ASSERT (max_size > 0); + + constraint->start_align = ped_alignment_duplicate (start_align); + constraint->end_align = ped_alignment_duplicate (end_align); + constraint->start_range = ped_geometry_duplicate (start_range); + constraint->end_range = ped_geometry_duplicate (end_range); + constraint->min_size = min_size; + constraint->max_size = max_size; + + return 1; +} + +/** + * Convenience wrapper for ped_constraint_init(). + * + * Allocates a new piece of memory and initializes the constraint. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new ( + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size) +{ + PedConstraint* constraint; + + constraint = (PedConstraint*) ped_malloc (sizeof (PedConstraint)); + if (!constraint) + goto error; + if (!ped_constraint_init (constraint, start_align, end_align, + start_range, end_range, min_size, max_size)) + goto error_free_constraint; + return constraint; + +error_free_constraint: + free (constraint); +error: + return NULL; +} + +/** + * Return a constraint that requires a region to be entirely contained inside + * \p max, and to entirely contain \p min. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_min_max ( + const PedGeometry* min, + const PedGeometry* max) +{ + PedGeometry start_range; + PedGeometry end_range; + + PED_ASSERT (min != NULL); + PED_ASSERT (max != NULL); + PED_ASSERT (ped_geometry_test_inside (max, min)); + + ped_geometry_init (&start_range, min->dev, max->start, + min->start - max->start + 1); + ped_geometry_init (&end_range, min->dev, min->end, + max->end - min->end + 1); + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &start_range, &end_range, + min->length, max->length); +} + +/** + * Return a constraint that requires a region to entirely contain \p min. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_min (const PedGeometry* min) +{ + PedGeometry full_dev; + + PED_ASSERT (min != NULL); + + ped_geometry_init (&full_dev, min->dev, 0, min->dev->length); + return ped_constraint_new_from_min_max (min, &full_dev); +} + +/** + * Return a constraint that requires a region to be entirely contained inside + * \p max. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_max (const PedGeometry* max) +{ + PED_ASSERT (max != NULL); + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + max, max, 1, max->length); +} + +/** + * Duplicate a constraint. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_duplicate (const PedConstraint* constraint) +{ + PED_ASSERT (constraint != NULL); + + return ped_constraint_new ( + constraint->start_align, + constraint->end_align, + constraint->start_range, + constraint->end_range, + constraint->min_size, + constraint->max_size); +} + +/** + * Return a constraint that requires a region to satisfy both \p a and \p b. + * + * Moreover, any region satisfying \p a and \p b will also satisfy the returned + * constraint. + * + * \return \c NULL if no solution could be found (note that \c NULL is a valid + * PedConstraint). + */ +PedConstraint* +ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b) +{ + PedAlignment* start_align; + PedAlignment* end_align; + PedGeometry* start_range; + PedGeometry* end_range; + PedSector min_size; + PedSector max_size; + PedConstraint* constraint; + + if (!a || !b) + return NULL; + + start_align = ped_alignment_intersect (a->start_align, b->start_align); + if (!start_align) + goto empty; + end_align = ped_alignment_intersect (a->end_align, b->end_align); + if (!end_align) + goto empty_destroy_start_align; + start_range = ped_geometry_intersect (a->start_range, b->start_range); + if (!start_range) + goto empty_destroy_end_align; + end_range = ped_geometry_intersect (a->end_range, b->end_range); + if (!end_range) + goto empty_destroy_start_range; + min_size = PED_MAX (a->min_size, b->min_size); + max_size = PED_MIN (a->max_size, b->max_size); + + constraint = ped_constraint_new ( + start_align, end_align, start_range, end_range, + min_size, max_size); + if (!constraint) + goto empty_destroy_end_range; + + ped_alignment_destroy (start_align); + ped_alignment_destroy (end_align); + ped_geometry_destroy (start_range); + ped_geometry_destroy (end_range); + return constraint; + +empty_destroy_end_range: + ped_geometry_destroy (end_range); +empty_destroy_start_range: + ped_geometry_destroy (start_range); +empty_destroy_end_align: + ped_alignment_destroy (end_align); +empty_destroy_start_align: + ped_alignment_destroy (start_align); +empty: + return NULL; +} + +/** + * Release the memory allocated for a PedConstraint constructed with + * ped_constraint_init(). + */ +void +ped_constraint_done (PedConstraint* constraint) +{ + PED_ASSERT (constraint != NULL); + + ped_alignment_destroy (constraint->start_align); + ped_alignment_destroy (constraint->end_align); + ped_geometry_destroy (constraint->start_range); + ped_geometry_destroy (constraint->end_range); +} + +/** + * Release the memory allocated for a PedConstraint constructed with + * ped_constraint_new(). + */ +void +ped_constraint_destroy (PedConstraint* constraint) +{ + if (constraint) { + ped_constraint_done (constraint); + free (constraint); + } +} + +/* + * Return the region within which the start must lie + * in order to satisfy a constriant. It takes into account + * constraint->start_range, constraint->min_size and constraint->max_size. + * All sectors in this range that also satisfy alignment requirements have + * an end, such that the (start, end) satisfy the constraint. + */ +static PedGeometry* +_constraint_get_canonical_start_range (const PedConstraint* constraint) +{ + PedSector first_end_soln; + PedSector last_end_soln; + PedSector min_start; + PedSector max_start; + PedGeometry start_min_max_range; + + if (constraint->min_size > constraint->max_size) + return NULL; + + first_end_soln = ped_alignment_align_down ( + constraint->end_align, constraint->end_range, + constraint->end_range->start); + last_end_soln = ped_alignment_align_up ( + constraint->end_align, constraint->end_range, + constraint->end_range->end); + if (first_end_soln == -1 || last_end_soln == -1 + || first_end_soln > last_end_soln + || last_end_soln < constraint->min_size) + return NULL; + + min_start = first_end_soln - constraint->max_size + 1; + if (min_start < 0) + min_start = 0; + max_start = last_end_soln - constraint->min_size + 1; + if (max_start < 0) + return NULL; + + ped_geometry_init ( + &start_min_max_range, constraint->start_range->dev, + min_start, max_start - min_start + 1); + + return ped_geometry_intersect (&start_min_max_range, + constraint->start_range); +} + +/* + * Return the nearest start that will have at least one other end that + * together satisfy the constraint. + */ +static PedSector +_constraint_get_nearest_start_soln (const PedConstraint* constraint, + PedSector start) +{ + PedGeometry* start_range; + PedSector result; + + start_range = _constraint_get_canonical_start_range (constraint); + if (!start_range) + return -1; + result = ped_alignment_align_nearest ( + constraint->start_align, start_range, start); + ped_geometry_destroy (start_range); + return result; +} + +/* + * Given a constraint and a start ("half of the solution"), find the + * range of all possible ends, such that all (start, end) are solutions + * to constraint (subject to additional alignment requirements). + */ +static PedGeometry* +_constraint_get_end_range (const PedConstraint* constraint, PedSector start) +{ + PedDevice* dev = constraint->end_range->dev; + PedSector first_min_max_end; + PedSector last_min_max_end; + PedGeometry end_min_max_range; + + if (start + constraint->min_size - 1 > dev->length - 1) + return NULL; + + first_min_max_end = start + constraint->min_size - 1; + last_min_max_end = start + constraint->max_size - 1; + if (last_min_max_end > dev->length - 1) + last_min_max_end = dev->length - 1; + + ped_geometry_init (&end_min_max_range, dev, + first_min_max_end, + last_min_max_end - first_min_max_end + 1); + + return ped_geometry_intersect (&end_min_max_range, + constraint->end_range); +} + +/* + * Given "constraint" and "start", find the end that is nearest to + * "end", such that ("start", the end) together form a solution to + * "constraint". + */ +static PedSector +_constraint_get_nearest_end_soln (const PedConstraint* constraint, + PedSector start, PedSector end) +{ + PedGeometry* end_range; + PedSector result; + + end_range = _constraint_get_end_range (constraint, start); + if (!end_range) + return -1; + + result = ped_alignment_align_nearest (constraint->end_align, end_range, + end); + ped_geometry_destroy (end_range); + return result; +} + +/** + * Return the nearest region to \p geom that satisfy a \p constraint. + * + * Note that "nearest" is somewhat ambiguous. This function makes + * no guarantees about how this ambiguity is resovled. + * + * \return PedGeometry, or NULL when a \p constrain cannot be satisfied + */ +PedGeometry* +ped_constraint_solve_nearest ( + const PedConstraint* constraint, const PedGeometry* geom) +{ + PedSector start; + PedSector end; + PedGeometry* result; + + if (constraint == NULL) + return NULL; + + PED_ASSERT (geom != NULL); + PED_ASSERT (constraint->start_range->dev == geom->dev); + + start = _constraint_get_nearest_start_soln (constraint, geom->start); + if (start == -1) + return NULL; + end = _constraint_get_nearest_end_soln (constraint, start, geom->end); + if (end == -1) + return NULL; + + result = ped_geometry_new (geom->dev, start, end - start + 1); + if (!result) + return NULL; + PED_ASSERT (ped_constraint_is_solution (constraint, result)); + return result; +} + +/** + * Find the largest region that satisfies a constraint. + * + * There might be more than one solution. This function makes no + * guarantees about which solution it will choose in this case. + */ +PedGeometry* +ped_constraint_solve_max (const PedConstraint* constraint) +{ + PedDevice* dev; + PedGeometry full_dev; + + if (!constraint) + return NULL; + dev = constraint->start_range->dev; + ped_geometry_init (&full_dev, dev, 0, dev->length - 1); + return ped_constraint_solve_nearest (constraint, &full_dev); +} + +/** + * Check whether \p geom satisfies the given constraint. + * + * \return \c 1 if it does. + **/ +int +ped_constraint_is_solution (const PedConstraint* constraint, + const PedGeometry* geom) +{ + PED_ASSERT (constraint != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_alignment_is_aligned (constraint->start_align, NULL, + geom->start)) + return 0; + if (!ped_alignment_is_aligned (constraint->end_align, NULL, geom->end)) + return 0; + if (!ped_geometry_test_sector_inside (constraint->start_range, + geom->start)) + return 0; + if (!ped_geometry_test_sector_inside (constraint->end_range, geom->end)) + return 0; + if (geom->length < constraint->min_size) + return 0; + if (geom->length > constraint->max_size) + return 0; + return 1; +} + +/** + * Return a constraint that any region on the given device will satisfy. + */ +PedConstraint* +ped_constraint_any (const PedDevice* dev) +{ + PedGeometry full_dev; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length)) + return NULL; + + return ped_constraint_new ( + ped_alignment_any, + ped_alignment_any, + &full_dev, + &full_dev, + 1, + dev->length); +} + +/** + * Return a constraint that only the given region will satisfy. + */ +PedConstraint* +ped_constraint_exact (const PedGeometry* geom) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_sector; + PedGeometry end_sector; + + ped_alignment_init (&start_align, geom->start, 0); + ped_alignment_init (&end_align, geom->end, 0); + ped_geometry_init (&start_sector, geom->dev, geom->start, 1); + ped_geometry_init (&end_sector, geom->dev, geom->end, 1); + + return ped_constraint_new (&start_align, &end_align, + &start_sector, &end_sector, 1, + geom->dev->length); +} + +/** + * @} + */ diff --git a/libparted/cs/geom.c b/libparted/cs/geom.c new file mode 100644 index 0000000..c7d92f1 --- /dev/null +++ b/libparted/cs/geom.c @@ -0,0 +1,492 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2000, 2005, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file geom.c */ + + +/** + * \addtogroup PedGeometry + * + * \brief PedGeometry represents a continuous region on a device. All addressing + * through a PedGeometry object is in terms of the start of the continuous + * region. + * + * The following conditions are always true on a PedGeometry object manipulated + * with the GNU Parted API: + * + * - <tt>start + length - 1 == end</tt> + * - <tt>length > 0</tt> + * - <tt>start >= 0</tt> + * - <tt>end < dev->length</tt> + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/** + * Initialize the previously allocated PedGeometry \p geom. + */ +int +ped_geometry_init (PedGeometry* geom, const PedDevice* dev, + PedSector start, PedSector length) +{ + PED_ASSERT (geom != NULL); + PED_ASSERT (dev != NULL); + + geom->dev = (PedDevice*) dev; + return ped_geometry_set (geom, start, length); +} + +/** + * Create a new PedGeometry object on \p disk, starting at \p start with a + * size of \p length sectors. + * + * \return NULL on failure. + */ +PedGeometry* +ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length) +{ + PedGeometry* geom; + + PED_ASSERT (dev != NULL); + + geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry)); + if (!geom) + goto error; + if (!ped_geometry_init (geom, dev, start, length)) + goto error_free_geom; + return geom; + +error_free_geom: + free (geom); +error: + return NULL; +} + +/** + * Duplicate a PedGeometry object. + * + * This function constructs a PedGeometry object that is an identical but + * independent copy of \p geom. Both the input, \p geom, and the output + * should be destroyed with ped_geometry_destroy() when they are no + * longer needed. + * + * \return NULL on failure. + */ +PedGeometry* +ped_geometry_duplicate (const PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + return ped_geometry_new (geom->dev, geom->start, geom->length); +} + +/** + * Return a PedGeometry object that refers to the intersection of + * \p a and \p b. + * + * This function constructs a PedGeometry object that describes the + * region that is common to both a and b. If there is no such common + * region, it returns NULL. (This situation is not treated as an + * error by much of GNU Parted.) + */ +PedGeometry* +ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b) +{ + PedSector start; + PedSector end; + + if (!a || !b || a->dev != b->dev) + return NULL; + + start = PED_MAX (a->start, b->start); + end = PED_MIN (a->end, b->end); + if (start > end) + return NULL; + + return ped_geometry_new (a->dev, start, end - start + 1); +} + +/** + * Destroy a PedGeometry object. + */ +void +ped_geometry_destroy (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + + free (geom); +} + +/** + * Assign a new \p start, \p end (implicitly) and \p length to \p geom. + * + * \p geom->end is calculated from \p start and \p length. + */ +int +ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length) +{ + PED_ASSERT (geom != NULL); + PED_ASSERT (geom->dev != NULL); + + if (length < 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have the end before the start!" + " (start sector=%jd length=%jd)"), start, length); + return 0; + } + if (start < 0 || start + length - 1 >= geom->dev->length) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have a partition outside the disk!")); + return 0; + } + + geom->start = start; + geom->length = length; + geom->end = start + length - 1; + + return 1; +} + +/** + * Assign a new start to \p geom without changing \p geom->end. + * + * \p geom->length is updated accordingly. + */ +int +ped_geometry_set_start (PedGeometry* geom, PedSector start) +{ + return ped_geometry_set (geom, start, geom->end - start + 1); +} + +/** + * Assign a new end to \p geom without changing \p geom->start. + * + * \p geom->length is updated accordingly. + */ +int +ped_geometry_set_end (PedGeometry* geom, PedSector end) +{ + return ped_geometry_set (geom, geom->start, end - geom->start + 1); +} +/** + * Test if \p a overlaps with \p b. + * + * That is, they lie on the same physical device, and they share + * the same physical region at least partially. + * + * \return 1 if \p a and \p b overlap. + */ +int +ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL); + PED_ASSERT (b != NULL); + + if (a->dev != b->dev) + return 0; + + if (a->start < b->start) + return a->end >= b->start; + else + return b->end >= a->start; +} + +/** + * Tests if \p b lies completely within \p a. That is, they lie on the same + * physical device, and all of the \p b's region is contained inside + * \p a's. + * + * \return 1 if the region \p b describes is contained entirely inside \p a +*/ +int +ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL); + PED_ASSERT (b != NULL); + + if (a->dev != b->dev) + return 0; + + return b->start >= a->start && b->end <= a->end; +} + +/** + * Tests if \a a and \p b refer to the same physical region. + * + * \return 1 if \p a and \p b describe the same regions + * + */ +int +ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL); + PED_ASSERT (b != NULL); + + return a->dev == b->dev + && a->start == b->start + && a->end == b->end; +} + +/** + * Tests if \p sector is inside \p geom. + * + * \return 1 if sector lies within the \p region that \p geom describes + */ +int +ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector) +{ + PED_ASSERT (geom != NULL); + + return sector >= geom->start && sector <= geom->end; +} + +/** + * Reads data from the region represented by \p geom. \p offset is the + * location from within the region, not from the start of the disk. + * \p count sectors are read into \p buffer. + * This is essentially equivalent to: + * \code + * ped_device_read (geom->disk->dev, buffer, geom->start + offset, count) + * \endcode + * + * \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of + * partition + * + * \return 0 on failure + */ +int +ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector offset, + PedSector count) +{ + PedSector real_start; + + PED_ASSERT (geom != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (offset >= 0); + PED_ASSERT (count >= 0); + + real_start = geom->start + offset; + + if (real_start + count - 1 > geom->end) + return 0; + + if (!ped_device_read (geom->dev, buffer, real_start, count)) + return 0; + return 1; +} + +/* Like ped_device_read, but read into malloc'd storage. */ +int +ped_geometry_read_alloc (const PedGeometry* geom, void** buffer, + PedSector offset, PedSector count) +{ + char *buf = ped_malloc (count * geom->dev->sector_size); + if (buf == NULL) + return 0; + int ok = ped_geometry_read (geom, buf, offset, count); + if (!ok) { + free (buf); + buf = NULL; + } + + *buffer = buf; + return ok; +} + +/** + * Flushes the cache on \p geom. + * + * This function flushes all write-behind caches that might be holding + * writes made by ped_geometry_write() to \p geom. It is slow, because + * it guarantees cache coherency among all relevant caches. + * + * \return 0 on failure + */ +int +ped_geometry_sync (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + return ped_device_sync (geom->dev); +} + +/** + * Flushes the cache on \p geom. + * + * This function flushes all write-behind caches that might be holding writes + * made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency + * with other caches that cache data in the region described by \p geom. + * If you need cache coherency, use ped_geometry_sync() instead. + * + * \return 0 on failure + */ +int +ped_geometry_sync_fast (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + return ped_device_sync_fast (geom->dev); +} + +/** + * Writes data into the region represented by \p geom. \p offset is the + * location from within the region, not from the start of the disk. + * \p count sectors are written. + * + * \return 0 on failure + */ +int +ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector offset, + PedSector count) +{ + int exception_status; + PedSector real_start; + + PED_ASSERT (geom != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (offset >= 0); + PED_ASSERT (count >= 0); + + real_start = geom->start + offset; + + if (real_start + count - 1 > geom->end) { + exception_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Attempt to write sectors %ld-%ld outside of " + "partition on %s."), + (long) offset, (long) (offset + count - 1), + geom->dev->path); + return exception_status == PED_EXCEPTION_IGNORE; + } + + if (!ped_device_write (geom->dev, buffer, real_start, count)) + return 0; + return 1; +} + +/** + * Checks for physical disk errors. \todo use ped_device_check() + * + * Checks a region for physical defects on \p geom. \p buffer is used + * for temporary storage for ped_geometry_check(), and has an undefined + * value. \p buffer is \p buffer_size sectors long. + * The region checked starts at \p offset sectors inside the + * region represented by \p geom, and is \p count sectors long. + * \p granularity specificies how sectors should be grouped + * together. The first bad sector to be returned will always be in + * the form: + * <tt>offset + n * granularity</tt> + * + * \return the first bad sector, or 0 if there were no physical errors + */ +PedSector +ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size, + PedSector offset, PedSector granularity, PedSector count, + PedTimer* timer) +{ + PedSector group; + PedSector i; + PedSector read_len; + + PED_ASSERT (geom != NULL); + PED_ASSERT (buffer != NULL); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("checking for bad blocks")); + +retry: + ped_exception_fetch_all(); + for (group = offset; group < offset + count; group += buffer_size) { + ped_timer_update (timer, 1.0 * (group - offset) / count); + read_len = PED_MIN (buffer_size, offset + count - group); + if (!ped_geometry_read (geom, buffer, group, read_len)) + goto found_error; + } + ped_exception_leave_all(); + ped_timer_update (timer, 1.0); + return 0; + +found_error: + ped_exception_catch(); + for (i = group; i + granularity < group + count; i += granularity) { + if (!ped_geometry_read (geom, buffer, i, granularity)) { + ped_exception_catch(); + ped_exception_leave_all(); + return i; + } + } + ped_exception_leave_all(); + goto retry; /* weird: failure on group read, but not individually */ +} + +/** + * This function takes a \p sector inside the region described by src, and + * returns that sector's address inside dst. This means that + * + * \code + * ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1) + * \endcode + * + * does the same thing as + * + * \code + * ped_geometry_read (src, buf, sector, 1) + * \endcode + * + * Clearly, this will only work if \p src and \p dst overlap. + * + * \return -1 if \p sector is not within \p dst's space, + * or \p sector's address inside \p dst + * + */ +PedSector +ped_geometry_map (const PedGeometry* dst, const PedGeometry* src, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (dst != NULL); + PED_ASSERT (src != NULL); + + if (!ped_geometry_test_sector_inside (src, sector)) + return -1; + if (dst->dev != src->dev) + return -1; + + result = src->start + sector - dst->start; + if (result < 0 || result > dst->length) + return -1; + + return result; +} + +/** @} */ diff --git a/libparted/cs/natmath.c b/libparted/cs/natmath.c new file mode 100644 index 0000000..0294436 --- /dev/null +++ b/libparted/cs/natmath.c @@ -0,0 +1,481 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * \file natmath.c + */ + +/** + * \addtogroup PedAlignment + * + * \brief Alignment constraint model. + * + * This part of libparted models alignment constraints. + * + * @{ + */ + +#include <config.h> +#include <stdlib.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/natmath.h> + +/* Arrrghhh! Why doesn't C have tuples? */ +typedef struct { + PedSector gcd; /* "converges" to the gcd */ + PedSector x; + PedSector y; +} EuclidTriple; + +static const PedAlignment _any = { + offset: 0, + grain_size: 1 +}; + +const PedAlignment* ped_alignment_any = &_any; +const PedAlignment* ped_alignment_none = NULL; + +/* This function returns "a mod b", the way C should have done it! + * Mathematicians prefer -3 mod 4 to be 3. Reason: division by N + * is all about adding or subtracting N, and we like our remainders + * to be between 0 and N - 1. + */ +static PedSector +abs_mod (PedSector a, PedSector b) +{ + if (a < 0) + return a % b + b; + else + return a % b; +} + +/* Rounds a number down to the closest number that is a multiple of + * grain_size. + */ +PedSector +ped_round_down_to (PedSector sector, PedSector grain_size) +{ + return sector - abs_mod (sector, grain_size); +} + +/* Rounds a number up to the closest number that is a multiple of + * grain_size. + */ +PedSector +ped_round_up_to (PedSector sector, PedSector grain_size) +{ + if (sector % grain_size) + return ped_round_down_to (sector, grain_size) + grain_size; + else + return sector; +} + +/* Rounds a number to the closest number that is a multiple of grain_size. */ +PedSector +ped_round_to_nearest (PedSector sector, PedSector grain_size) +{ + if (sector % grain_size > grain_size/2) + return ped_round_up_to (sector, grain_size); + else + return ped_round_down_to (sector, grain_size); +} + +/* This function returns the largest number that divides both a and b. + * It uses the ancient Euclidean algorithm. + */ +PedSector +ped_greatest_common_divisor (PedSector a, PedSector b) +{ + PED_ASSERT (a >= 0); + PED_ASSERT (b >= 0); + + /* Put the arguments in the "right" format. (Recursive calls made by + * this function are always in the right format.) + */ + if (b > a) + return ped_greatest_common_divisor (b, a); + + if (b) + return ped_greatest_common_divisor (b, a % b); + else + return a; +} + +/** + * Initialize a preallocated piece of memory for an alignment object + * (used by PedConstraint). + * + * The object will represent all sectors \e s for which the equation + * <tt>s = offset + X * grain_size</tt> holds. + */ +int +ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size) +{ + PED_ASSERT (align != NULL); + + if (grain_size < 0) + return 0; + + if (grain_size) + align->offset = abs_mod (offset, grain_size); + else + align->offset = offset; + align->grain_size = grain_size; + + return 1; +} + +/** + * Return an alignment object (used by PedConstraint), representing all + * PedSector's that are of the form <tt>offset + X * grain_size</tt>. + */ +PedAlignment* +ped_alignment_new (PedSector offset, PedSector grain_size) +{ + PedAlignment* align; + + align = (PedAlignment*) ped_malloc (sizeof (PedAlignment)); + if (!align) + goto error; + + if (!ped_alignment_init (align, offset, grain_size)) + goto error_free_align; + + return align; + +error_free_align: + free (align); +error: + return NULL; +} + +/** + * Free up memory associated with \p align. + */ +void +ped_alignment_destroy (PedAlignment* align) +{ + free (align); +} + +/** + * Return a duplicate of \p align. + */ +PedAlignment* +ped_alignment_duplicate (const PedAlignment* align) +{ + if (!align) + return NULL; + return ped_alignment_new (align->offset, align->grain_size); +} + +/* the extended Euclid algorithm. + * + * input: + * a and b, a > b + * + * output: + * gcd, x and y, such that: + * + * gcd = greatest common divisor of a and b + * gcd = x*a + y*b + */ +static EuclidTriple +extended_euclid (int a, int b) +{ + EuclidTriple result; + EuclidTriple tmp; + + if (b == 0) { + result.gcd = a; + result.x = 1; + result.y = 0; + return result; + } + + tmp = extended_euclid (b, a % b); + result.gcd = tmp.gcd; + result.x = tmp.y; + result.y = tmp.x - (a/b) * tmp.y; + return result; +} + +/** + * This function computes a PedAlignment object that describes the + * intersection of two alignments. That is, a sector satisfies the + * new alignment object if and only if it satisfies both of the original + * ones. (See ped_alignment_is_aligned() for the meaning of "satisfies") + * + * Apart from the trivial cases (where one or both of the alignment objects + * constraints have no sectors that satisfy them), this is what we're trying to + * do: + * - two input constraints: \p a and \p b. + * - the new grain_size is going to be the lowest common multiple of + * \p a->grain_size and \p b->grain_size + * - hard part - solve the simultaneous equations, for offset, where offset, + * X and Y are variables. (Note: offset can be obtained from either X or Y, + * by substituing into either equation) + * + * \code + * offset = \p a->offset + X * \p a->grain_size (1) + * offset = \p b->offset + Y * \p b->grain_size (2) + * \endcode + * + * or, abbreviated: + * + * \code + * o = Ao + X*Ag (1) + * o = Bo + Y*Bg (2) + * + * => Ao + X*Ag = Bo + Y*Bg (1) = (2) + * X*Ag - Y*Bg = Bo - Ao (3) + * \endcode + * + * As it turns out, there only exists a solution if (Bo - Ao) is a multiple + * of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are + * multiples of the GCD. + * + * Proof: + * + * \code + * A * Ag + B * Bg + * = A * (\p a * gcd) + B * (\p b * gcd) + * = gcd * (A * \p a + B * \p b) + * \endcode + * + * gcd is a factor of the linear combination. QED + * + * Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd) + * with Euclid's extended algorithm. Then, we just multiply through by + * (Bo - Ao) / gcd to get (3). + * + * i.e. + * \code + * A * Ag + B * Bg = gcd + * A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd + * X*Ag - Y*Bg = Bo - Ao (3) + * + * X = A*(Bo-Ao)/gcd + * Y = - B*(Bo-Ao)/gcd + * \endcode + * + * then: + * \code + * o = Ao + X*Ag (1) + * = Ao + A*(Bo-Ao)/gcd*Ag + * o = Bo + Y*Bg (2) + * = Bo - B*(Bo-Ao)/gcd*Ag + * \endcode + * + * Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring + * this algorithm out :-) + * + * \note Returned \c NULL is a valid PedAlignment object, and can be used + for ped_alignment_*() function. + * + * \return a PedAlignment on success, \c NULL on failure + */ +PedAlignment* +ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b) +{ + PedSector new_grain_size; + PedSector new_offset; + PedSector delta_on_gcd; + EuclidTriple gcd_factors; + + + if (!a || !b) + return NULL; + + /*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)", + a->offset, a->grain_size, b->offset, b->grain_size); + */ + + if (a->grain_size < b->grain_size) { + const PedAlignment* tmp; + tmp = a; a = b; b = tmp; + } + + /* weird/trivial case: where the solution space for "a" or "b" is + * either empty or contains exactly one solution + */ + if (a->grain_size == 0 && b->grain_size == 0) { + if (a->offset == b->offset) + return ped_alignment_duplicate (a); + else + return NULL; + } + + /* general case */ + gcd_factors = extended_euclid (a->grain_size, b->grain_size); + + delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd; + new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size; + new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd; + + /* inconsistency => no solution */ + if (new_offset + != b->offset - gcd_factors.y * delta_on_gcd * b->grain_size) + return NULL; + + return ped_alignment_new (new_offset, new_grain_size); +} + +/* This function returns the sector closest to "sector" that lies inside + * geom and satisfies the alignment constraint. + */ +static PedSector +_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PED_ASSERT (align != NULL); + + if (!align->grain_size) { + if (ped_alignment_is_aligned (align, geom, sector) + && (!geom || ped_geometry_test_sector_inside (geom, + sector))) + return sector; + else + return -1; + } + + if (sector < geom->start) + sector += ped_round_up_to (geom->start - sector, + align->grain_size); + if (sector > geom->end) + sector -= ped_round_up_to (sector - geom->end, + align->grain_size); + + if (!ped_geometry_test_sector_inside (geom, sector)) + return -1; + return sector; +} + +/** + * This function returns the closest sector to \p sector that lies inside + * \p geom that satisfies the given alignment constraint \p align. It prefers + * sectors that are beyond \p sector (are not smaller than \p sector), + * but does not guarantee that this. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (align != NULL); + + if (!align->grain_size) + result = align->offset; + else + result = ped_round_up_to (sector - align->offset, + align->grain_size) + + align->offset; + + if (geom) + result = _closest_inside_geometry (align, geom, result); + return result; +} + +/** + * This function returns the closest sector to \p sector that lies inside + * \p geom that satisfies the given alignment constraint \p align. It prefers + * sectors that are before \p sector (are not larger than \p sector), + * but does not guarantee that this. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (align != NULL); + + if (!align->grain_size) + result = align->offset; + else + result = ped_round_down_to (sector - align->offset, + align->grain_size) + + align->offset; + + if (geom) + result = _closest_inside_geometry (align, geom, result); + return result; +} + +/* Returns either a or b, depending on which is closest to "sector". */ +static PedSector +closest (PedSector sector, PedSector a, PedSector b) +{ + if (a == -1) + return b; + if (b == -1) + return a; + + if (abs (sector - a) < abs (sector - b)) + return a; + else + return b; +} + +/** + * This function returns the sector that is closest to \p sector, + * satisfies the \p align constraint and lies inside \p geom. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PED_ASSERT (align != NULL); + + return closest (sector, ped_alignment_align_up (align, geom, sector), + ped_alignment_align_down (align, geom, sector)); +} + +/** + * This function returns 1 if \p sector satisfies the alignment + * constraint \p align and lies inside \p geom. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + if (!align) + return 0; + + if (geom && !ped_geometry_test_sector_inside (geom, sector)) + return 0; + + if (align->grain_size) + return (sector - align->offset) % align->grain_size == 0; + else + return sector == align->offset; +} + +/** + * @} + */ diff --git a/libparted/debug.c b/libparted/debug.c new file mode 100644 index 0000000..08d523e --- /dev/null +++ b/libparted/debug.c @@ -0,0 +1,114 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#ifdef DEBUG + +#if HAVE_BACKTRACE +#include <execinfo.h> +#endif + +static void default_handler ( const int level, const char* file, int line, + const char* function, const char* msg ); +static PedDebugHandler* debug_handler = &default_handler; + + +/** + * Default debug handler. + * Will print all information to stderr. + */ +static void default_handler ( const int level, const char* file, int line, + const char* function, const char* msg ) +{ + fprintf ( stderr, "[%d] %s:%d (%s): %s\n", + level, file, line, function, msg ); +} + +/** + * Send a debug message. + * Do not call this directly -- use PED_DEBUG() instead. + * + * level log level, 0 ~= "print definitely" + */ +void ped_debug ( const int level, const char* file, int line, + const char* function, const char* msg, ... ) +{ + va_list arg_list; + char* msg_concat = ped_malloc(8192); + + va_start ( arg_list, msg ); + vsnprintf ( msg_concat, 8192, msg, arg_list ); + va_end ( arg_list ); + + debug_handler ( level, file, line, function, msg_concat ); + + free ( msg_concat ); +} + +/* + * handler debug handler; NULL for default handler + */ +void ped_debug_set_handler ( PedDebugHandler* handler ) +{ + debug_handler = ( handler ? handler : default_handler ); +} + +/* + * Check an assertion. + * Do not call this directly -- use PED_ASSERT() instead. + */ +void ped_assert (const char* cond_text, + const char* file, int line, const char* function) +{ +#if HAVE_BACKTRACE + /* Print backtrace stack */ + void *stack[20]; + char **strings, **string; + int size = backtrace(stack, 20); + strings = backtrace_symbols(stack, size); + + if (strings) { + printf(_("Backtrace has %d calls on stack:\n"), size); + + for (string = strings; size > 0; size--, string++) + printf(" %d: %s\n", size, *string); + + free(strings); + } +#endif + + /* Throw the exception */ + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_FATAL, + _("Assertion (%s) at %s:%d in function %s() failed."), + cond_text, file, line, function); + abort (); +} + +#endif /* DEBUG */ diff --git a/libparted/device.c b/libparted/device.c new file mode 100644 index 0000000..738b320 --- /dev/null +++ b/libparted/device.c @@ -0,0 +1,565 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999 - 2001, 2005, 2007-2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file device.c */ + +/** + * \addtogroup PedDevice + * + * \brief Device access. + * + * When ped_device_probe_all() is called, libparted attempts to detect all + * devices. It constructs a list which can be accessed with + * ped_device_get_next(). + * + * If you want to use a device that isn't on the list, use + * ped_device_get(). Also, there may be OS-specific constructors, for creating + * devices from file descriptors, stores, etc. For example, + * ped_device_new_from_store(). + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#include <limits.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> + +#include "architecture.h" + +static PedDevice* devices; /* legal advice says: initialized to NULL, + under section 6.7.8 part 10 + of ISO/EIC 9899:1999 */ + +static void +_device_register (PedDevice* dev) +{ + PedDevice* walk; + for (walk = devices; walk && walk->next; walk = walk->next); + if (walk) + walk->next = dev; + else + devices = dev; + dev->next = NULL; +} + +static void +_device_unregister (PedDevice* dev) +{ + PedDevice* walk; + PedDevice* last = NULL; + + for (walk = devices; walk != NULL; last = walk, walk = walk->next) { + if (walk == dev) break; + } + + /* This function may be called twice for the same device if a + libparted user explictly removes the device from the cache using + ped_device_cache_remove(), we get called and it then becomes the + user's responsibility to free the PedDevice by calling + ped_device_destroy(). + ped_device_destroy() will then call us a second time, so if the + device is not found in the list do nothing. */ + if (walk == NULL) + return; + + if (last) + last->next = dev->next; + else + devices = dev->next; +} + +/** + * Returns the next device that was detected by ped_device_probe_all(), or + * calls to ped_device_get_next(). + * If dev is NULL, returns the first device. + * + * \return NULL if dev is the last device. + */ +PedDevice* +ped_device_get_next (const PedDevice* dev) +{ + if (dev) + return dev->next; + else + return devices; +} + +void +_ped_device_probe (const char* path) +{ + PedDevice* dev; + + PED_ASSERT (path != NULL); + + ped_exception_fetch_all (); + dev = ped_device_get (path); + if (!dev) + ped_exception_catch (); + ped_exception_leave_all (); +} + +/** + * Attempts to detect all devices. + */ +void +ped_device_probe_all () +{ + ped_architecture->dev_ops->probe_all (); +} + +/** + * Close/free all devices. + * Called by ped_done(), so you do not need to worry about it. + */ +void +ped_device_free_all () +{ + while (devices) + ped_device_destroy (devices); +} + +/** + * Gets the device "name", where name is usually the block device, e.g. + * /dev/sdb. If the device wasn't detected with ped_device_probe_all(), + * an attempt will be made to detect it again. If it is found, it will + * be added to the list. + */ +PedDevice* +ped_device_get (const char* path) +{ + PedDevice* walk; + char* normal_path = NULL; + + PED_ASSERT (path != NULL); + /* Don't canonicalize /dev/mapper paths, see tests/symlink.c */ + if (strncmp (path, "/dev/mapper/", 12)) + normal_path = canonicalize_file_name (path); + if (!normal_path) + /* Well, maybe it is just that the file does not exist. + * Try it anyway. */ + normal_path = strdup (path); + if (!normal_path) + return NULL; + + for (walk = devices; walk != NULL; walk = walk->next) { + if (!strcmp (walk->path, normal_path)) { + free (normal_path); + return walk; + } + } + + walk = ped_architecture->dev_ops->_new (normal_path); + free (normal_path); + if (!walk) + return NULL; + _device_register (walk); + return walk; +} + +/** + * Destroys a device and removes it from the device list, and frees + * all resources associated with the device (all resources allocated + * when the device was created). + */ +void +ped_device_destroy (PedDevice* dev) +{ + _device_unregister (dev); + + while (dev->open_count) { + if (!ped_device_close (dev)) + break; + } + + ped_architecture->dev_ops->destroy (dev); +} + +void +ped_device_cache_remove(PedDevice *dev) +{ + _device_unregister (dev); +} + +int +ped_device_is_busy (PedDevice* dev) +{ + return ped_architecture->dev_ops->is_busy (dev); +} + +/** + * Attempt to open a device to allow use of read, write and sync functions. + * + * The meaning of "open" is architecture-dependent. Apart from requesting + * access to the device from the operating system, it does things like flushing + * caches. + * \note May allocate resources. Any resources allocated here will + * be freed by a final ped_device_close(). (ped_device_open() may be + * called multiple times -- it's a ref-count-like mechanism) + * + * \return zero on failure + */ +int +ped_device_open (PedDevice* dev) +{ + int status; + + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + if (dev->open_count) + status = ped_architecture->dev_ops->refresh_open (dev); + else + status = ped_architecture->dev_ops->open (dev); + if (status) + dev->open_count++; + return status; +} + +/** + * Close dev. + * If this is the final close, then resources allocated by + * ped_device_open() are freed. + * + * \return zero on failure + */ +int +ped_device_close (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + if (--dev->open_count) + return ped_architecture->dev_ops->refresh_close (dev); + else + return ped_architecture->dev_ops->close (dev); +} + +/** + * Begins external access mode. External access mode allows you to + * safely do IO on the device. If a PedDevice is open, then you should + * not do any IO on that device, e.g. by calling an external program + * like e2fsck, unless you put it in external access mode. You should + * not use any libparted commands that do IO to a device, e.g. + * ped_file_system_{open|resize|copy}, ped_disk_{read|write}), while + * a device is in external access mode. + * Also, you should not ped_device_close() a device, while it is + * in external access mode. + * Note: ped_device_begin_external_access_mode() does things like + * tell the kernel to flush its caches. + * + * Close a device while pretending it is still open. + * This is useful for temporarily suspending libparted access to the device + * in order for an external program to access it. + * (Running external programs while the device is open can cause cache + * coherency problems.) + * + * In particular, this function keeps track of dev->open_count, so that + * reference counting isn't screwed up. + * + * \return zero on failure. + */ +int +ped_device_begin_external_access (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + dev->external_mode = 1; + if (dev->open_count) + return ped_architecture->dev_ops->close (dev); + else + return 1; +} + +/** + * \brief Complementary function to ped_device_begin_external_access. + * + * \note does things like tell the kernel to flush the device's cache. + * + * \return zero on failure. + */ +int +ped_device_end_external_access (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (dev->external_mode); + + dev->external_mode = 0; + if (dev->open_count) + return ped_architecture->dev_ops->open (dev); + else + return 1; +} + +/** + * \internal Read count sectors from dev into buffer, beginning with sector + * start. + * + * \return zero on failure. + */ +int +ped_device_read (const PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return (ped_architecture->dev_ops->read) (dev, buffer, start, count); +} + +/** + * \internal Write count sectors from buffer to dev, starting at sector + * start. + * + * \return zero on failure. + * + * \sa PedDevice::sector_size + * \sa PedDevice::phys_sector_size + */ +int +ped_device_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return (ped_architecture->dev_ops->write) (dev, buffer, start, count); +} + +PedSector +ped_device_check (PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return (ped_architecture->dev_ops->check) (dev, buffer, start, count); +} + +/** + * \internal Flushes all write-behind caches that might be holding up + * writes. + * It is slow because it guarantees cache coherency among all relevant caches. + * + * \return zero on failure + */ +int +ped_device_sync (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return ped_architecture->dev_ops->sync (dev); +} + +/** + * \internal Flushes all write-behind caches that might be holding writes. + * \warning Does NOT ensure cache coherency with other caches. + * If you need cache coherency, use ped_device_sync() instead. + * + * \return zero on failure + */ +int +ped_device_sync_fast (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return ped_architecture->dev_ops->sync_fast (dev); +} + +/** + * Get a constraint that represents hardware requirements on geometry. + * This function will return a constraint representing the limits imposed + * by the size of the disk, it will *not* provide any alignment constraints. + * + * Alignment constraints may be desirable when using media that have a physical + * sector size that is a multiple of the logical sector size, as in this case + * proper partition alignment can benefit disk performance signigicantly. + * When you want a constraint with alignment info, use + * ped_device_get_minimal_aligned_constraint() or + * ped_device_get_optimal_aligned_constraint(). + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * constraint. + */ +PedConstraint* +ped_device_get_constraint (const PedDevice* dev) +{ + PedGeometry *s, *e; + PedConstraint* c = ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + s = ped_geometry_new (dev, 0, dev->length), + e = ped_geometry_new (dev, 0, dev->length), + 1, dev->length); + + free (s); + free (e); + return c; +} + +static PedConstraint* +_ped_device_get_aligned_constraint(const PedDevice *dev, + PedAlignment* start_align) +{ + PedAlignment *end_align = NULL; + PedGeometry *whole_dev_geom = NULL; + PedConstraint *c = NULL; + + if (start_align) { + end_align = ped_alignment_new(start_align->offset - 1, + start_align->grain_size); + if (!end_align) + goto free_start_align; + } + + whole_dev_geom = ped_geometry_new (dev, 0, dev->length); + + if (start_align) + c = ped_constraint_new (start_align, end_align, + whole_dev_geom, whole_dev_geom, + 1, dev->length); + else + c = ped_constraint_new (ped_alignment_any, ped_alignment_any, + whole_dev_geom, whole_dev_geom, + 1, dev->length); + + free (whole_dev_geom); + free (end_align); +free_start_align: + free (start_align); + return c; +} + +/** + * Get a constraint that represents hardware requirements on geometry and + * alignment. + * + * This function will return a constraint representing the limits imposed + * by the size of the disk and the minimal alignment requirements for proper + * performance of the disk. + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * constraint. + */ +PedConstraint* +ped_device_get_minimal_aligned_constraint(const PedDevice *dev) +{ + return _ped_device_get_aligned_constraint(dev, + ped_device_get_minimum_alignment(dev)); +} + +/** + * Get a constraint that represents hardware requirements on geometry and + * alignment. + * + * This function will return a constraint representing the limits imposed + * by the size of the disk and the alignment requirements for optimal + * performance of the disk. + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * constraint. + */ +PedConstraint* +ped_device_get_optimal_aligned_constraint(const PedDevice *dev) +{ + return _ped_device_get_aligned_constraint(dev, + ped_device_get_optimum_alignment(dev)); +} + +/** + * Get an alignment that represents minimum hardware requirements on alignment. + * When for example using media that has a physical sector size that is a + * multiple of the logical sector size, it is desirable to have disk accesses + * (and thus partitions) properly aligned. Having partitions not aligned to + * the minimum hardware requirements may lead to a performance penalty. + * + * The returned alignment describes the alignment for the start sector of the + * partition, the end sector should be aligned too, to get the end sector + * alignment decrease the returned alignment's offset by 1. + * + * \return the minimum alignment of partition start sectors, or NULL if this + * information is not available. + */ +PedAlignment* +ped_device_get_minimum_alignment(const PedDevice *dev) +{ + PedAlignment *align = NULL; + + if (ped_architecture->dev_ops->get_minimum_alignment) + align = ped_architecture->dev_ops->get_minimum_alignment(dev); + + if (align == NULL) + align = ped_alignment_new(0, + dev->phys_sector_size / dev->sector_size); + + return align; +} + +/** + * Get an alignment that represents the hardware requirements for optimal + * performance. + * + * The returned alignment describes the alignment for the start sector of the + * partition, the end sector should be aligned too, to get the end sector + * alignment decrease the returned alignment's offset by 1. + * + * \return the optimal alignment of partition start sectors, or NULL if this + * information is not available. + */ +PedAlignment* +ped_device_get_optimum_alignment(const PedDevice *dev) +{ + PedAlignment *align = NULL; + + if (ped_architecture->dev_ops->get_optimum_alignment) + align = ped_architecture->dev_ops->get_optimum_alignment(dev); + + /* If the arch specific code could not give as an alignment + return a default value based on the type of device. */ + if (align == NULL) { + switch (dev->type) { + case PED_DEVICE_DASD: + align = ped_device_get_minimum_alignment(dev); + break; + default: + /* Align to a grain of 1MiB (like vista / win7) */ + align = ped_alignment_new(0, + (PED_DEFAULT_ALIGNMENT + / dev->sector_size)); + } + } + + return align; +} + +/** @} */ diff --git a/libparted/disk.c b/libparted/disk.c new file mode 100644 index 0000000..f994478 --- /dev/null +++ b/libparted/disk.c @@ -0,0 +1,2444 @@ + /* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2003, 2005, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file disk.c */ + +/** + * \addtogroup PedDisk + * + * \brief Disk label access. + * + * Most programs will need to use ped_disk_new() or ped_disk_new_fresh() to get + * anything done. A PedDisk is always associated with a device and has a + * partition table. There are different types of partition tables (or disk + * labels). These are represented by the PedDiskType enumeration. + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <stdbool.h> + +#include "architecture.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +# define N_(String) (String) +#else +# define _(String) (String) +# define N_(String) (String) +#endif /* ENABLE_NLS */ + +/* UPDATE MODE functions */ +#ifdef DEBUG +static int _disk_check_sanity (PedDisk* disk); +#endif +static int _disk_push_update_mode (PedDisk* disk); +static int _disk_pop_update_mode (PedDisk* disk); +static int _disk_raw_insert_before (PedDisk* disk, PedPartition* loc, + PedPartition* part); +static int _disk_raw_insert_after (PedDisk* disk, PedPartition* loc, + PedPartition* part); +static int _disk_raw_remove (PedDisk* disk, PedPartition* part); +static int _disk_raw_add (PedDisk* disk, PedPartition* part); + +static PedDiskType* disk_types = NULL; + +PedDisk* +ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type); + +void +ped_disk_type_register (PedDiskType* disk_type) +{ + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops != NULL); + PED_ASSERT (disk_type->name != NULL); + + disk_type->next = disk_types; + disk_types = disk_type; +} + +void +ped_disk_type_unregister (PedDiskType* disk_type) +{ + PedDiskType* walk; + PedDiskType* last = NULL; + + PED_ASSERT (disk_types != NULL); + PED_ASSERT (disk_type != NULL); + + for (walk = disk_types; walk && walk != disk_type; + last = walk, walk = walk->next); + + PED_ASSERT (walk != NULL); + if (last) + ((struct _PedDiskType*) last)->next = disk_type->next; + else + disk_types = disk_type->next; +} + +/** + * Return the next disk type registers, after "type". If "type" is + * NULL, returns the first disk type. + * + * \return Next disk; NULL if "type" is the last registered disk type. + */ +PedDiskType* +ped_disk_type_get_next (PedDiskType const *type) +{ + if (type) + return type->next; + else + return disk_types; +} + +/** + * Return the disk type with a name of "name". + * + * \return Disk type; NULL if no match. + */ +PedDiskType* +ped_disk_type_get (const char* name) +{ + PedDiskType* walk = NULL; + + PED_ASSERT (name != NULL); + + for (walk = ped_disk_type_get_next (NULL); walk; + walk = ped_disk_type_get_next (walk)) + if (strcasecmp (walk->name, name) == 0) + break; + + return walk; +} + +/** + * Return the type of partition table detected on "dev". + * + * \return Type; NULL if none was detected. + */ +PedDiskType* +ped_disk_probe (PedDevice* dev) +{ + PedDiskType* walk = NULL; + + PED_ASSERT (dev != NULL); + + if (!ped_device_open (dev)) + return NULL; + + ped_exception_fetch_all (); + for (walk = ped_disk_type_get_next (NULL); walk; + walk = ped_disk_type_get_next (walk)) + { + if (getenv ("PARTED_DEBUG")) { + fprintf (stderr, "probe label: %s\n", + walk->name); + fflush (stderr); + } + if (walk->ops->probe (dev)) + break; + } + + if (ped_exception) + ped_exception_catch (); + ped_exception_leave_all (); + + ped_device_close (dev); + return walk; +} + +/** + * Read the partition table off a device (if one is found). + * + * \warning May modify \p dev->cylinders, \p dev->heads and \p dev->sectors + * if the partition table indicates that the existing values + * are incorrect. + * + * \return A new \link _PedDisk PedDisk \endlink object; + * NULL on failure (e.g. partition table not detected). + */ +PedDisk* +ped_disk_new (PedDevice* dev) +{ + PedDiskType* type; + PedDisk* disk; + + PED_ASSERT (dev != NULL); + + if (!ped_device_open (dev)) + goto error; + + type = ped_disk_probe (dev); + if (!type) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s: unrecognised disk label"), + dev->path); + goto error_close_dev; + } + disk = ped_disk_new_fresh (dev, type); + if (!disk) + goto error_close_dev; + if (!type->ops->read (disk)) + goto error_destroy_disk; + ped_device_close (dev); + return disk; + +error_destroy_disk: + ped_disk_destroy (disk); +error_close_dev: + ped_device_close (dev); +error: + return NULL; +} + +static int +_add_duplicate_part (PedDisk* disk, PedPartition* old_part) +{ + PedPartition* new_part; + int ret; + + new_part = disk->type->ops->partition_duplicate (old_part); + if (!new_part) + goto error; + new_part->disk = disk; + + if (!_disk_push_update_mode (disk)) + goto error_destroy_new_part; + ret = _disk_raw_add (disk, new_part); + if (!_disk_pop_update_mode (disk)) + goto error_destroy_new_part; + if (!ret) + goto error_destroy_new_part; +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + goto error_destroy_new_part; +#endif + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +/** + * Clone a \link _PedDisk PedDisk \endlink object. + * + * \return Deep copy of \p old_disk, NULL on failure. + */ +PedDisk* +ped_disk_duplicate (const PedDisk* old_disk) +{ + PedDisk* new_disk; + PedPartition* old_part; + + PED_ASSERT (old_disk != NULL); + PED_ASSERT (!old_disk->update_mode); + PED_ASSERT (old_disk->type->ops->duplicate != NULL); + PED_ASSERT (old_disk->type->ops->partition_duplicate != NULL); + + new_disk = old_disk->type->ops->duplicate (old_disk); + if (!new_disk) + goto error; + + if (!_disk_push_update_mode (new_disk)) + goto error_destroy_new_disk; + for (old_part = ped_disk_next_partition (old_disk, NULL); old_part; + old_part = ped_disk_next_partition (old_disk, old_part)) { + if (ped_partition_is_active (old_part)) { + if (!_add_duplicate_part (new_disk, old_part)){ + _disk_pop_update_mode (new_disk); + goto error_destroy_new_disk; + } + } + } + if (!_disk_pop_update_mode (new_disk)) + goto error_destroy_new_disk; + + return new_disk; + +error_destroy_new_disk: + ped_disk_destroy (new_disk); +error: + return NULL; +} + +/** + * Create a new partition table on \p dev. + * + * This new partition table is only created in-memory, and nothing is written + * to disk until ped_disk_commit_to_dev() is called. + * + * \return The newly constructed \link _PedDisk PedDisk \endlink, + * NULL on failure. + */ +PedDisk* +ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type) +{ + PedDisk* disk; + + PED_ASSERT (dev != NULL); + PED_ASSERT (type != NULL); + PED_ASSERT (type->ops->alloc != NULL); + PedCHSGeometry* bios_geom = &dev->bios_geom; + PED_ASSERT (bios_geom->sectors != 0); + PED_ASSERT (bios_geom->heads != 0); + + disk = type->ops->alloc (dev); + if (!disk) + goto error; + if (!_disk_pop_update_mode (disk)) + goto error_destroy_disk; + PED_ASSERT (disk->update_mode == 0); + + return disk; + +error_destroy_disk: + ped_disk_destroy (disk); +error: + return NULL; +} + +PedDisk* +_ped_disk_alloc (const PedDevice* dev, const PedDiskType* disk_type) +{ + PedDisk* disk; + + disk = (PedDisk*) ped_malloc (sizeof (PedDisk)); + if (!disk) + goto error; + + disk->dev = (PedDevice*)dev; + disk->type = disk_type; + disk->update_mode = 1; + disk->part_list = NULL; + return disk; + +error: + return NULL; +} + +void +_ped_disk_free (PedDisk* disk) +{ + _disk_push_update_mode (disk); + ped_disk_delete_all (disk); + free (disk); +} + +/** + * Close \p disk. + * + * What this function does depends on the PedDiskType of \p disk, + * but you can generally assume that outstanding writes are flushed + * (this mainly means that _ped_disk_free is called). + */ +void +ped_disk_destroy (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (!disk->update_mode); + + disk->type->ops->free (disk); +} + +/** + * Tell the operating system kernel about the partition table layout + * of \p disk. + * + * This is rather loosely defined: for example, on old versions of Linux, + * it simply calls the BLKRRPART ioctl, which tells the kernel to + * reread the partition table. On newer versions (2.4.x), it will + * use the new blkpg interface to tell Linux where each partition + * starts/ends, etc. In this case, Linux does not need to have support for + * a specific type of partition table. + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit_to_os (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + if (!ped_device_open (disk->dev)) + goto error; + if (!ped_architecture->disk_ops->disk_commit (disk)) + goto error_close_dev; + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/** + * Write the changes made to the in-memory description + * of a partition table to the device. + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit_to_dev (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (!disk->update_mode); + + if (!disk->type->ops->write) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("This libparted doesn't have write support for " + "%s. Perhaps it was compiled read-only."), + disk->type->name); + goto error; + } + + if (!ped_device_open (disk->dev)) + goto error; + + if (!disk->type->ops->write (disk)) + goto error_close_dev; + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/* + * This function writes the in-memory changes to a partition table to + * disk and informs the operating system of the changes. + * + * \note Equivalent to calling first ped_disk_commit_to_dev(), then + * ped_disk_commit_to_os(). + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit (PedDisk* disk) +{ + /* Open the device here, so that the underlying fd is not closed + between commit_to_dev and commit_to_os (closing causes unwanted + udev events to be sent under Linux). */ + if (!ped_device_open (disk->dev)) + goto error; + + if (!ped_disk_commit_to_dev (disk)) + goto error_close_dev; + + if (!ped_disk_commit_to_os (disk)) + goto error_close_dev; + + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** + * Check whether a partition is mounted or busy in some + * other way. + * + * \note An extended partition is busy if any logical partitions are mounted. + * + * \return \c 1 if busy. + */ +int +ped_partition_is_busy (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + return ped_architecture->disk_ops->partition_is_busy (part); +} + +/** + * Return a path that can be used to address the partition in the + * operating system. + */ +char* +ped_partition_get_path (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + return ped_architecture->disk_ops->partition_get_path (part); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +/** + * Perform a sanity check on a partition table. + * + * \note The check performed is generic (i.e. it does not depends on the label + * type of the disk. + * + * \throws PED_EXCEPTION_WARNING if a partition type ID does not match the file + * system on it. + * + * \return 0 if the check fails, 1 otherwise. + */ +int +ped_disk_check (const PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + const PedFileSystemType* fs_type = walk->fs_type; + PedGeometry* geom; + PedSector length_error; + PedSector max_length_error; + + if (!ped_partition_is_active (walk) || !fs_type) + continue; + + geom = ped_file_system_probe_specific (fs_type, &walk->geom); + if (!geom) + continue; + + length_error = abs (walk->geom.length - geom->length); + max_length_error = PED_MAX (4096, walk->geom.length / 100); + bool ok = (ped_geometry_test_inside (&walk->geom, geom) + && length_error <= max_length_error); + char *fs_size = ped_unit_format (disk->dev, geom->length); + ped_geometry_destroy (geom); + if (!ok) { + char* part_size = ped_unit_format (disk->dev, + walk->geom.length); + PedExceptionOption choice; + choice = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition %d is %s, but the file system is " + "%s."), + walk->num, part_size, fs_size); + + free (part_size); + + free (fs_size); + fs_size = NULL; + + if (choice != PED_EXCEPTION_IGNORE) + return 0; + } + free (fs_size); + } + + return 1; +} + +/** + * This function checks if a particular type of partition table supports + * a feature. + * + * \return 1 if \p disk_type supports \p feature, 0 otherwise. + */ +int +ped_disk_type_check_feature (const PedDiskType* disk_type, + PedDiskTypeFeature feature) +{ + return (disk_type->features & feature) != 0; +} + +/** + * Get the number of primary partitions. + */ +int +ped_disk_get_primary_partition_count (const PedDisk* disk) +{ + PedPartition* walk; + int count = 0; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) + && ! (walk->type & PED_PARTITION_LOGICAL)) + count++; + } + + return count; +} + +/** + * Get the highest available partition number on \p disk. + */ +int +ped_disk_get_last_partition_num (const PedDisk* disk) +{ + PedPartition* walk; + int highest = -1; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->num > highest) + highest = walk->num; + } + + return highest; +} + +/** + * Get the highest supported partition number on \p disk. + * + * \return 0 if call fails. 1 otherwise. + */ +bool +ped_disk_get_max_supported_partition_count(const PedDisk* disk, int* supported) +{ + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->type->ops->get_max_supported_partition_count != NULL); + + return disk->type->ops->get_max_supported_partition_count(disk, supported); +} + +/** + * Get the alignment needed for partition boundaries on this disk. + * The returned alignment describes the alignment for the start sector of the + * partition, for all disklabel types which require alignment, except Sun + * disklabels, the end sector must be aligned too. To get the end sector + * alignment decrease the PedAlignment offset by 1. + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * alignment. + */ +PedAlignment* +ped_disk_get_partition_alignment(const PedDisk *disk) +{ + /* disklabel handlers which don't need alignment don't define this */ + if (!disk->type->ops->get_partition_alignment) + return ped_alignment_duplicate(ped_alignment_any); + + return disk->type->ops->get_partition_alignment(disk); +} + +/** + * Get the maximum number of (primary) partitions the disk label supports. + * + * For example, MacIntosh partition maps can have different sizes, + * and accordingly support a different number of partitions. + */ +int +ped_disk_get_max_primary_partition_count (const PedDisk* disk) +{ + PED_ASSERT (disk->type != NULL); + PED_ASSERT (disk->type->ops->get_max_primary_partition_count != NULL); + + return disk->type->ops->get_max_primary_partition_count (disk); +} + +/** + * Set the state (\c 1 or \c 0) of a flag on a disk. + * + * \note It is an error to call this on an unavailable flag -- use + * ped_disk_is_flag_available() to determine which flags are available + * for a given disk label. + * + * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this + * label. + */ +int +ped_disk_set_flag(PedDisk *disk, PedDiskFlag flag, int state) +{ + int ret; + + PED_ASSERT (disk != NULL); + + PedDiskOps *ops = disk->type->ops; + + if (!_disk_push_update_mode(disk)) + return 0; + + if (!ped_disk_is_flag_available(disk, flag)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "The flag '%s' is not available for %s disk labels.", + ped_disk_flag_get_name(flag), + disk->type->name); + _disk_pop_update_mode(disk); + return 0; + } + + ret = ops->disk_set_flag(disk, flag, state); + + if (!_disk_pop_update_mode (disk)) + return 0; + + return ret; +} + +/** + * Get the state (\c 1 or \c 0) of a flag on a disk. + */ +int +ped_disk_get_flag(const PedDisk *disk, PedDiskFlag flag) +{ + PED_ASSERT (disk != NULL); + + PedDiskOps *ops = disk->type->ops; + + if (!ped_disk_is_flag_available(disk, flag)) + return 0; + + return ops->disk_get_flag(disk, flag); +} + +/** + * Check whether a given flag is available on a disk. + * + * \return \c 1 if the flag is available. + */ +int +ped_disk_is_flag_available(const PedDisk *disk, PedDiskFlag flag) +{ + PED_ASSERT (disk != NULL); + + PedDiskOps *ops = disk->type->ops; + + if (!ops->disk_is_flag_available) + return 0; + + return ops->disk_is_flag_available(disk, flag); +} + +/** + * Returns a name for a \p flag, e.g. PED_DISK_CYLINDER_ALIGNMENT will return + * "cylinder_alignment". + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + */ +const char * +ped_disk_flag_get_name(PedDiskFlag flag) +{ + switch (flag) { + case PED_DISK_CYLINDER_ALIGNMENT: + return N_("cylinder_alignment"); + + default: + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unknown disk flag, %d."), + flag); + return NULL; + } +} + +/** + * Returns the flag associated with \p name. + * + * \p name can be the English + * string, or the translation for the native language. + */ +PedDiskFlag +ped_disk_flag_get_by_name(const char *name) +{ + PedDiskFlag flag; + + for (flag = ped_disk_flag_next(0); flag; + flag = ped_disk_flag_next(flag)) { + const char *flag_name = ped_disk_flag_get_name(flag); + if (strcasecmp(name, flag_name) == 0 + || strcasecmp(name, _(flag_name)) == 0) + return flag; + } + + return 0; +} + +/** + * Iterates through all disk flags. + * + * ped_disk_flag_next(0) returns the first flag + * + * \return the next flag, or 0 if there are no more flags + */ +PedDiskFlag +ped_disk_flag_next(PedDiskFlag flag) +{ + return (flag + 1) % (PED_DISK_LAST_FLAG + 1); +} + +/** + * \internal We turned a really nasty bureaucracy problem into an elegant maths + * problem :-) Basically, there are some constraints to a partition's + * geometry: + * + * (1) it must start and end on a "disk" block, determined by the disk label + * (not the hardware). (constraint represented by a PedAlignment) + * + * (2) if we're resizing a partition, we MIGHT need to keep each block aligned. + * Eg: if an ext2 file system has 4k blocks, then we can only move the start + * by a multiple of 4k. (constraint represented by a PedAlignment) + * + * (3) we need to keep the start and end within the device's physical + * boundaries. (constraint represented by a PedGeometry) + * + * Satisfying (1) and (2) simultaneously required a bit of fancy maths ;-) See + * ped_alignment_intersect() + * + * The application of these constraints is in disk_*.c's *_partition_align() + * function. + */ +static int +_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->num != -1); + PED_ASSERT (part->disk != NULL); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops->partition_align != NULL); + PED_ASSERT (part->disk->update_mode); + + return disk_type->ops->partition_align (part, constraint); +} + +static int +_partition_enumerate (PedPartition* part) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops->partition_enumerate != NULL); + + return disk_type->ops->partition_enumerate (part); +} + +/** + * Gives all the (active) partitions a number. It should preserve the numbers + * and orders as much as possible. + */ +static int +ped_disk_enumerate_partitions (PedDisk* disk) +{ + PedPartition* walk; + int i; + int end; + + PED_ASSERT (disk != NULL); + +/* first "sort" already-numbered partitions. (e.g. if a logical partition + * is removed, then all logical partitions that were number higher MUST be + * renumbered) + */ + end = ped_disk_get_last_partition_num (disk); + for (i=1; i<=end; i++) { + walk = ped_disk_get_partition (disk, i); + if (walk) { + if (!_partition_enumerate (walk)) + return 0; + } + } + +/* now, number un-numbered partitions */ + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) && walk->num == -1) { + if (!_partition_enumerate (walk)) + return 0; + } + } + + return 1; +} + +static int +_disk_remove_metadata (PedDisk* disk) +{ + PedPartition* walk = NULL; + PedPartition* next; + + PED_ASSERT (disk != NULL); + + next = ped_disk_next_partition (disk, walk); + + while (next) { + walk = next; + while (1) { + next = ped_disk_next_partition (disk, next); + if (!next || next->type & PED_PARTITION_METADATA) + break; + } + if (walk->type & PED_PARTITION_METADATA) + ped_disk_delete_partition (disk, walk); + } + return 1; +} + +static int +_disk_alloc_metadata (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + if (!disk->update_mode) + _disk_remove_metadata (disk); + + return disk->type->ops->alloc_metadata (disk); +} + +static int +_disk_remove_freespace (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + + walk = ped_disk_next_partition (disk, NULL); + for (; walk; walk = next) { + next = ped_disk_next_partition (disk, walk); + + if (walk->type & PED_PARTITION_FREESPACE) { + _disk_raw_remove (disk, walk); + ped_partition_destroy (walk); + } + } + + return 1; +} + +static int +_alloc_extended_freespace (PedDisk* disk) +{ + PedSector last_end; + PedPartition* walk; + PedPartition* last; + PedPartition* free_space; + PedPartition* extended_part; + + extended_part = ped_disk_extended_partition (disk); + if (!extended_part) + return 1; + + last_end = extended_part->geom.start; + last = NULL; + + for (walk = extended_part->part_list; walk; walk = walk->next) { + if (walk->geom.start > last_end + 1) { + free_space = ped_partition_new ( + disk, + PED_PARTITION_FREESPACE + | PED_PARTITION_LOGICAL, + NULL, + last_end + 1, walk->geom.start - 1); + _disk_raw_insert_before (disk, walk, free_space); + } + + last = walk; + last_end = last->geom.end; + } + + if (last_end < extended_part->geom.end) { + free_space = ped_partition_new ( + disk, + PED_PARTITION_FREESPACE | PED_PARTITION_LOGICAL, + NULL, + last_end + 1, extended_part->geom.end); + + if (last) + return _disk_raw_insert_after (disk, last, free_space); + else + extended_part->part_list = free_space; + } + + return 1; +} + +static int +_disk_alloc_freespace (PedDisk* disk) +{ + PedSector last_end; + PedPartition* walk; + PedPartition* last; + PedPartition* free_space; + + if (!_disk_remove_freespace (disk)) + return 0; + if (!_alloc_extended_freespace (disk)) + return 0; + + last = NULL; + last_end = -1; + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->geom.start > last_end + 1) { + free_space = ped_partition_new (disk, + PED_PARTITION_FREESPACE, NULL, + last_end + 1, walk->geom.start - 1); + _disk_raw_insert_before (disk, walk, free_space); + } + + last = walk; + last_end = last->geom.end; + } + + if (last_end < disk->dev->length - 1) { + free_space = ped_partition_new (disk, + PED_PARTITION_FREESPACE, NULL, + last_end + 1, disk->dev->length - 1); + if (last) + return _disk_raw_insert_after (disk, last, free_space); + else + disk->part_list = free_space; + } + + return 1; +} + +/** + * Update mode: used when updating the internal representation of the partition + * table. In update mode, the metadata and freespace placeholder/virtual + * partitions are removed, making it much easier for various manipulation + * routines... + */ +static int +_disk_push_update_mode (PedDisk* disk) +{ + if (!disk->update_mode) { +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + + _disk_remove_freespace (disk); + disk->update_mode++; + _disk_remove_metadata (disk); + +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + } else { + disk->update_mode++; + } + return 1; +} + +static int +_disk_pop_update_mode (PedDisk* disk) +{ + PED_ASSERT (disk->update_mode); + + if (disk->update_mode == 1) { + /* re-allocate metadata BEFORE leaving update mode, to prevent infinite + * recursion (metadata allocation requires update mode) + */ +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + + _disk_alloc_metadata (disk); + disk->update_mode--; + _disk_alloc_freespace (disk); + +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + } else { + disk->update_mode--; + } + return 1; +} + +/** @} */ + +/** + * \addtogroup PedPartition + * + * \brief Partition access. + * + * @{ + */ + +PedPartition* +_ped_partition_alloc (const PedDisk* disk, PedPartitionType type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + PED_ASSERT (disk != NULL); + + part = (PedPartition*) ped_malloc (sizeof (PedPartition)); + if (!part) + goto error; + + part->prev = NULL; + part->next = NULL; + + part->disk = (PedDisk*) disk; + if (!ped_geometry_init (&part->geom, disk->dev, start, end - start + 1)) + goto error_free_part; + + part->num = -1; + part->type = type; + part->part_list = NULL; + part->fs_type = fs_type; + + return part; + +error_free_part: + free (part); +error: + return NULL; +} + +void +_ped_partition_free (PedPartition* part) +{ + free (part); +} + +int +_ped_partition_attempt_align (PedPartition* part, + const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + goto fail; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + if (!solution) + goto fail_free_intersection; + ped_geometry_set (&part->geom, solution->start, solution->length); + ped_geometry_destroy (solution); + ped_constraint_destroy (intersection); + return 1; + +fail_free_intersection: + ped_constraint_destroy (intersection); +fail: + return 0; +} + +/** + * Create a new \link _PedPartition PedPartition \endlink on \p disk. + * + * \param type One of \p PED_PARTITION_NORMAL, \p PED_PARTITION_EXTENDED, + * \p PED_PARTITION_LOGICAL. + * + * \note The constructed partition is not added to <tt>disk</tt>'s + * partition table. Use ped_disk_add_partition() to do this. + * + * \return A new \link _PedPartition PedPartition \endlink object, + * NULL on failure. + * + * \throws PED_EXCEPTION_ERROR if \p type is \p EXTENDED or \p LOGICAL but the + * label does not support this concept. + */ +PedPartition* +ped_partition_new (const PedDisk* disk, PedPartitionType type, + const PedFileSystemType* fs_type, PedSector start, + PedSector end) +{ + int supports_extended; + PedPartition* part; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->type->ops->partition_new != NULL); + + supports_extended = ped_disk_type_check_feature (disk->type, + PED_DISK_TYPE_EXTENDED); + + if (!supports_extended + && (type == PED_PARTITION_EXTENDED + || type == PED_PARTITION_LOGICAL)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s disk labels do not support extended " + "partitions."), + disk->type->name); + goto error; + } + + part = disk->type->ops->partition_new (disk, type, fs_type, start, end); + if (!part) + goto error; + + if (fs_type || part->type == PED_PARTITION_EXTENDED) { + if (!ped_partition_set_system (part, fs_type)) + goto error_destroy_part; + } + return part; + +error_destroy_part: + ped_partition_destroy (part); +error: + return NULL; +} + +/** + * Destroy a \link _PedPartition PedPartition \endlink object. + * + * \note Should not be called on a partition that is in a partition table. + * Use ped_disk_delete_partition() instead. + */ +void +ped_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->type->ops->partition_new != NULL); + + part->disk->type->ops->partition_destroy (part); +} + + +/** + * Return whether or not the partition is "active". + * + * A partition is active if \p part->type is neither \p PED_PARTITION_METADATA + * nor \p PED_PARTITION_FREE. + */ +int +ped_partition_is_active (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + return !(part->type & PED_PARTITION_FREESPACE + || part->type & PED_PARTITION_METADATA); +} + +/** + * Set the state (\c 1 or \c 0) of a flag on a partition. + * + * Flags are disk label specific, although they have a global + * "namespace": the flag PED_PARTITION_BOOT, for example, roughly means + * "this" partition is bootable". But this means different things on different + * disk labels (and may not be defined on some disk labels). For example, + * on MS-DOS disk labels, there can only be one boot partition, and this + * refers to the partition that will be booted from on startup. On PC98 + * disk labels, the user can choose from any bootable partition on startup. + * + * \note It is an error to call this on an unavailable flag -- use + * ped_partition_is_flag_available() to determine which flags are available + * for a given disk label. + * + * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this + * label. + */ +int +ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedDiskOps* ops; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + ops = part->disk->type->ops; + PED_ASSERT (ops->partition_set_flag != NULL); + PED_ASSERT (ops->partition_is_flag_available != NULL); + + if (!ops->partition_is_flag_available (part, flag)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "The flag '%s' is not available for %s disk labels.", + ped_partition_flag_get_name (flag), + part->disk->type->name); + return 0; + } + + return ops->partition_set_flag (part, flag, state); +} + +/** + * Get the state (\c 1 or \c 0) of a flag on a partition. + * + * See ped_partition_set_flag() for conditions that must hold. + * + * \todo Where's the check for flag availability? + */ +int +ped_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->type->ops->partition_get_flag != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + return part->disk->type->ops->partition_get_flag (part, flag); +} + +/** + * Check whether a given flag is available on a partition. + * + * \return \c 1 if the flag is available. + */ +int +ped_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->type->ops->partition_is_flag_available != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + return part->disk->type->ops->partition_is_flag_available (part, flag); +} + +/** + * Sets the system type on the partition to \p fs_type. + * + * \note The file system may be opened, to get more information about the + * file system, e.g. to determine if it's FAT16 or FAT32. + * + * \return \c 0 on failure. + */ +int +ped_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL); + PED_ASSERT (ped_partition_is_active (part)); + PED_ASSERT (part->disk != NULL); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops != NULL); + PED_ASSERT (disk_type->ops->partition_set_system != NULL); + + return disk_type->ops->partition_set_system (part, fs_type); +} + +static int +_assert_partition_name_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_PARTITION_NAME)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support partition names.", + disk_type->name); + return 0; + } + return 1; +} + +/** + * Sets the name of a partition. + * + * \note This will only work if the disk label supports it. + * You can use + * \code + * ped_disk_type_check_feature (part->disk->type, PED_DISK_TYPE_PARTITION_NAME); + * \endcode + * to check whether this feature is enabled for a label. + * + * \note \p name will not be modified by libparted. It can be freed + * by the caller immediately after ped_partition_set_name() is called. + * + * \return \c 1 on success, \c 0 otherwise. + */ +int +ped_partition_set_name (PedPartition* part, const char* name) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + PED_ASSERT (name != NULL); + + if (!_assert_partition_name_feature (part->disk->type)) + return 0; + + PED_ASSERT (part->disk->type->ops->partition_set_name != NULL); + part->disk->type->ops->partition_set_name (part, name); + return 1; +} + +/** + * Returns the name of a partition \p part. This will only work if the disk + * label supports it. + * + * \note The returned string should not be modified. It should + * not be referenced after the partition is destroyed. + */ +const char* +ped_partition_get_name (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_name_feature (part->disk->type)) + return NULL; + + PED_ASSERT (part->disk->type->ops->partition_get_name != NULL); + return part->disk->type->ops->partition_get_name (part); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +PedPartition* +ped_disk_extended_partition (const PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->type == PED_PARTITION_EXTENDED) + break; + } + return walk; +} + +/** + * Return the next partition after \p part on \p disk. If \p part is \c NULL, + * return the first partition. If \p part is the last partition, returns + * \c NULL. If \p part is an extended partition, returns the first logical + * partition. If this is called repeatedly passing the return value as \p part, + * a depth-first traversal is executed. + * + * \return The next partition, \c NULL if no more partitions left. + */ +PedPartition* +ped_disk_next_partition (const PedDisk* disk, const PedPartition* part) +{ + PED_ASSERT (disk != NULL); + + if (!part) + return disk->part_list; + if (part->type == PED_PARTITION_EXTENDED) + return part->part_list ? part->part_list : part->next; + if (part->next) + return part->next; + if (part->type & PED_PARTITION_LOGICAL) + return ped_disk_extended_partition (disk)->next; + return NULL; +} + +/** @} */ + +#ifdef DEBUG +static int +_disk_check_sanity (PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; walk = walk->next) { + PED_ASSERT (!(walk->type & PED_PARTITION_LOGICAL)); + PED_ASSERT (!walk->prev || walk->prev->next == walk); + } + + if (!ped_disk_extended_partition (disk)) + return 1; + + for (walk = ped_disk_extended_partition (disk)->part_list; walk; + walk = walk->next) { + PED_ASSERT (walk->type & PED_PARTITION_LOGICAL); + if (walk->prev) + PED_ASSERT (walk->prev->next == walk); + } + return 1; +} +#endif + +/** + * Returns the partition numbered \p num. + * + * \return \c NULL if the specified partition does not exist. + */ +PedPartition* +ped_disk_get_partition (const PedDisk* disk, int num) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE)) + return walk; + } + + return NULL; +} + +/** + * Returns the partition that contains sect. If sect lies within a logical + * partition, then the logical partition is returned (not the extended + * partition). + */ +PedPartition* +ped_disk_get_partition_by_sector (const PedDisk* disk, PedSector sect) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_geometry_test_sector_inside (&walk->geom, sect) + && walk->type != PED_PARTITION_EXTENDED) + return walk; + } + + /* should never get here, unless sect is outside of disk's useable + * part, or we're in "update mode", and the free space place-holders + * have been removed with _disk_remove_freespace() + */ + return NULL; +} + +/** + * Return the maximum representable length (in sectors) of a + * partition on disk \disk. + */ +PedSector +ped_disk_max_partition_length (const PedDisk* disk) +{ + return disk->type->ops->max_length (); +} + +/** + * Return the maximum representable start sector of a + * partition on disk \disk. + */ +PedSector +ped_disk_max_partition_start_sector (const PedDisk* disk) +{ + return disk->type->ops->max_start_sector (); +} + +/* I'm beginning to agree with Sedgewick :-/ */ +static int +_disk_raw_insert_before (PedDisk* disk, PedPartition* loc, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (loc != NULL); + PED_ASSERT (part != NULL); + + part->prev = loc->prev; + part->next = loc; + if (part->prev) { + part->prev->next = part; + } else { + if (loc->type & PED_PARTITION_LOGICAL) + ped_disk_extended_partition (disk)->part_list = part; + else + disk->part_list = part; + } + loc->prev = part; + + return 1; +} + +static int +_disk_raw_insert_after (PedDisk* disk, PedPartition* loc, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (loc != NULL); + PED_ASSERT (part != NULL); + + part->prev = loc; + part->next = loc->next; + if (loc->next) + loc->next->prev = part; + loc->next = part; + + return 1; +} + +static int +_disk_raw_remove (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (part->prev) { + part->prev->next = part->next; + if (part->next) + part->next->prev = part->prev; + } else { + if (part->type & PED_PARTITION_LOGICAL) { + ped_disk_extended_partition (disk)->part_list + = part->next; + } else { + disk->part_list = part->next; + } + if (part->next) + part->next->prev = NULL; + } + + return 1; +} + +/* + *UPDATE MODE ONLY + */ +static int +_disk_raw_add (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + PedPartition* last; + PedPartition* ext_part; + + PED_ASSERT (disk->update_mode); + + ext_part = ped_disk_extended_partition (disk); + + last = NULL; + walk = (part->type & PED_PARTITION_LOGICAL) ? + ext_part->part_list : disk->part_list; + + for (; walk; last = walk, walk = walk->next) { + if (walk->geom.start > part->geom.end) + break; + } + + if (walk) { + return _disk_raw_insert_before (disk, walk, part); + } else { + if (last) { + return _disk_raw_insert_after (disk, last, part); + } else { + if (part->type & PED_PARTITION_LOGICAL) + ext_part->part_list = part; + else + disk->part_list = part; + } + } + + return 1; +} + +static PedConstraint* +_partition_get_overlap_constraint (PedPartition* part, PedGeometry* geom) +{ + PedSector min_start; + PedSector max_end; + PedPartition* walk; + PedGeometry free_space; + + PED_ASSERT (part->disk->update_mode); + PED_ASSERT (part->geom.dev == geom->dev); + + if (part->type & PED_PARTITION_LOGICAL) { + PedPartition* ext_part; + + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL); + + min_start = ext_part->geom.start; + max_end = ext_part->geom.end; + walk = ext_part->part_list; + } else { + min_start = 0; + max_end = part->disk->dev->length - 1; + walk = part->disk->part_list; + } + + while (walk != NULL + && (walk->geom.start < geom->start + || min_start >= walk->geom.start)) { + if (walk != part) + min_start = walk->geom.end + 1; + walk = walk->next; + } + + if (walk == part) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1; + + if (min_start >= max_end) + return NULL; + + ped_geometry_init (&free_space, part->disk->dev, + min_start, max_end - min_start + 1); + return ped_constraint_new_from_max (&free_space); +} + +/* + * Returns \c 0 if the partition, \p part overlaps with any partitions on the + * \p disk. The geometry of \p part is taken to be \p geom, NOT \p part->geom + * (the idea here is to check if \p geom is valid, before changing \p part). + * + * This is useful for seeing if a resized partitions new geometry is going to + * fit, without the existing geomtry getting in the way. + * + * Note: overlap with an extended partition is also allowed, provided that + * \p geom lies completely inside the extended partition. + */ +static int +_disk_check_part_overlaps (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + for (walk = ped_disk_next_partition (disk, NULL); walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->type & PED_PARTITION_FREESPACE) + continue; + if (walk == part) + continue; + if (part->type & PED_PARTITION_EXTENDED + && walk->type & PED_PARTITION_LOGICAL) + continue; + + if (ped_geometry_test_overlap (&walk->geom, &part->geom)) { + if (walk->type & PED_PARTITION_EXTENDED + && part->type & PED_PARTITION_LOGICAL + && ped_geometry_test_inside (&walk->geom, + &part->geom)) + continue; + return 0; + } + } + + return 1; +} + +static int +_partition_check_basic_sanity (PedDisk* disk, PedPartition* part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + + PED_ASSERT (part->disk == disk); + + PED_ASSERT (part->geom.start >= 0); + PED_ASSERT (part->geom.end < disk->dev->length); + PED_ASSERT (part->geom.start <= part->geom.end); + + if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED) + && (part->type == PED_PARTITION_EXTENDED + || part->type == PED_PARTITION_LOGICAL)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s disk labels don't support logical or extended " + "partitions."), + disk->type->name); + return 0; + } + + if (ped_partition_is_active (part) + && ! (part->type & PED_PARTITION_LOGICAL)) { + if (ped_disk_get_primary_partition_count (disk) + 1 + > ped_disk_get_max_primary_partition_count (disk)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many primary partitions.")); + return 0; + } + } + + if ((part->type & PED_PARTITION_LOGICAL) && !ext_part) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add a logical partition to %s, because " + "there is no extended partition."), + disk->dev->path); + return 0; + } + + return 1; +} + +static int +_check_extended_partition (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + PedPartition* ext_part; + + PED_ASSERT (disk != NULL); + ext_part = ped_disk_extended_partition (disk); + if (!ext_part) ext_part = part; + PED_ASSERT (ext_part != NULL); + + if (part != ext_part) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have more than one extended partition on %s."), + disk->dev->path); + return 0; + } + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_geometry_test_inside (&ext_part->geom, &walk->geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have logical partitions outside of " + "the extended partition.")); + return 0; + } + } + return 1; +} + +static int +_check_partition (PedDisk* disk, PedPartition* part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + + PED_ASSERT (part->geom.start <= part->geom.end); + + if (part->type == PED_PARTITION_EXTENDED) { + if (!_check_extended_partition (disk, part)) + return 0; + } + + if (part->type & PED_PARTITION_LOGICAL + && !ped_geometry_test_inside (&ext_part->geom, &part->geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have a logical partition outside of the " + "extended partition on %s."), + disk->dev->path); + return 0; + } + + if (!_disk_check_part_overlaps (disk, part)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + return 0; + } + + if (! (part->type & PED_PARTITION_LOGICAL) + && ext_part && ext_part != part + && ped_geometry_test_inside (&ext_part->geom, &part->geom)) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Can't have a primary partition inside an extended " + "partition.")); + return 0; + } + + if (!(part->type & PED_PARTITION_METADATA)) + if (!disk->type->ops->partition_check(part)) + return 0; + + return 1; +} + +/** + * Adds PedPartition \p part to PedPartition \p disk. + * + * \warning The partition's geometry may be changed, subject to \p constraint. + * You could set \p constraint to <tt>ped_constraint_exact(&part->geom)</tt>, + * but many partition table schemes have special requirements on the start + * and end of partitions. Therefore, having an overly strict constraint + * will probably mean that this function will fail (in which + * case \p part will be left unmodified) + * \p part is assigned a number (\p part->num) in this process. + * + * \return \c 0 on failure. + */ +int +ped_disk_add_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedConstraint* overlap_constraint = NULL; + PedConstraint* constraints = NULL; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (!_partition_check_basic_sanity (disk, part)) + return 0; + + if (!_disk_push_update_mode (disk)) + return 0; + + if (ped_partition_is_active (part)) { + overlap_constraint + = _partition_get_overlap_constraint (part, &part->geom); + constraints = ped_constraint_intersect (overlap_constraint, + constraint); + + if (!constraints && constraint) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + goto error; + } + + if (!_partition_enumerate (part)) + goto error; + if (!_partition_align (part, constraints)) + goto error; + } + /* FIXME: when _check_partition fails, we end up leaking PART + at least for DVH partition tables. Simply calling + ped_partition_destroy(part) here fixes it for DVH, but + causes trouble for other partition types. Similarly, + reordering these two checks, putting _check_partition after + _disk_raw_add induces an infinite loop. */ + if (!_check_partition (disk, part)) + goto error; + if (!_disk_raw_add (disk, part)) + goto error; + + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + if (!_disk_pop_update_mode (disk)) + return 0; +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + return 1; + +error: + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + _disk_pop_update_mode (disk); + return 0; +} + +/** + * Removes PedPartition \p part from PedDisk \p disk. + * + * If \p part is an extended partition, it must not contain any logical + * partitions. \p part is *NOT* destroyed. The caller must call + * ped_partition_destroy(), or use ped_disk_delete_partition() instead. + * + * \return \c 0 on error. + */ +int +ped_disk_remove_partition (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (!_disk_push_update_mode (disk)) + return 0; + PED_ASSERT (part->part_list == NULL); + _disk_raw_remove (disk, part); + if (!_disk_pop_update_mode (disk)) + return 0; + ped_disk_enumerate_partitions (disk); + return 1; +} + +static int +ped_disk_delete_all_logical (PedDisk* disk); + +/** + * Removes \p part from \p disk, and destroys \p part. + * + * \return \c 0 on failure. + */ +int +ped_disk_delete_partition (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (!_disk_push_update_mode (disk)) + return 0; + if (part->type == PED_PARTITION_EXTENDED) + ped_disk_delete_all_logical (disk); + ped_disk_remove_partition (disk, part); + ped_partition_destroy (part); + if (!_disk_pop_update_mode (disk)) + return 0; + + return 1; +} + +static int +ped_disk_delete_all_logical (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + PedPartition* ext_part; + + PED_ASSERT (disk != NULL); + ext_part = ped_disk_extended_partition (disk); + PED_ASSERT (ext_part != NULL); + + for (walk = ext_part->part_list; walk; walk = next) { + next = walk->next; + + if (!ped_disk_delete_partition (disk, walk)) + return 0; + } + return 1; +} + +/** + * Removes and destroys all partitions on \p disk. + * + * \return \c 0 on failure. + */ +int +ped_disk_delete_all (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + + PED_ASSERT (disk != NULL); + + if (!_disk_push_update_mode (disk)) + return 0; + + for (walk = disk->part_list; walk; walk = next) { + next = walk->next; + + if (!ped_disk_delete_partition (disk, walk)) { + _disk_pop_update_mode(disk); + return 0; + } + } + + if (!_disk_pop_update_mode (disk)) + return 0; + + return 1; +} + +/** + * Sets the geometry of \p part (i.e. change a partitions location). This can + * fail for many reasons, e.g. can't overlap with other partitions. If it + * does fail, \p part will remain unchanged. Returns \c 0 on failure. \p part's + * geometry may be set to something different from \p start and \p end subject + * to \p constraint. + * + * \warning The constraint warning from ped_disk_add_partition() applies. + * + * \note this function does not modify the contents of the partition. You need + * to call ped_file_system_resize() separately. + */ +int +ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint, + PedSector start, PedSector end) +{ + PedConstraint* overlap_constraint = NULL; + PedConstraint* constraints = NULL; + PedGeometry old_geom; + PedGeometry new_geom; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk == disk); + + old_geom = part->geom; + ped_geometry_init (&new_geom, part->geom.dev, start, end - start + 1); + + if (!_disk_push_update_mode (disk)) + return 0; + + overlap_constraint + = _partition_get_overlap_constraint (part, &new_geom); + constraints = ped_constraint_intersect (overlap_constraint, constraint); + if (!constraints && constraint) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + goto error_pop_update_mode; + } + + part->geom = new_geom; + if (!_partition_align (part, constraints)) + goto error_pop_update_mode; + if (!_check_partition (disk, part)) + goto error_pop_update_mode; + + /* remove and add, to ensure the ordering gets updated if necessary */ + _disk_raw_remove (disk, part); + _disk_raw_add (disk, part); + + if (!_disk_pop_update_mode (disk)) + goto error; + + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + return 1; + +error_pop_update_mode: + _disk_pop_update_mode (disk); +error: + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + part->geom = old_geom; + return 0; +} + +/** + * Grow PedPartition \p part geometry to the maximum possible subject to + * \p constraint. The new geometry will be a superset of the old geometry. + * + * \return 0 on failure + */ +int +ped_disk_maximize_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedGeometry old_geom; + PedSector global_min_start; + PedSector global_max_end; + PedSector new_start; + PedSector new_end; + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (part->type & PED_PARTITION_LOGICAL) { + PED_ASSERT (ext_part != NULL); + global_min_start = ext_part->geom.start; + global_max_end = ext_part->geom.end; + } else { + global_min_start = 0; + global_max_end = disk->dev->length - 1; + } + + old_geom = part->geom; + + if (!_disk_push_update_mode (disk)) + return 0; + + if (part->prev) + new_start = part->prev->geom.end + 1; + else + new_start = global_min_start; + + if (part->next) + new_end = part->next->geom.start - 1; + else + new_end = global_max_end; + + if (!ped_disk_set_partition_geom (disk, part, constraint, new_start, + new_end)) + goto error; + + if (!_disk_pop_update_mode (disk)) + return 0; + return 1; + +error: + constraint_any = ped_constraint_any (disk->dev); + ped_disk_set_partition_geom (disk, part, constraint_any, + old_geom.start, old_geom.end); + ped_constraint_destroy (constraint_any); + _disk_pop_update_mode (disk); + return 0; +} + +/** + * Get the maximum geometry \p part can be grown to, subject to + * \p constraint. + * + * \return \c NULL on failure. + */ +PedGeometry* +ped_disk_get_max_partition_geometry (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedGeometry old_geom; + PedGeometry* max_geom; + PedConstraint* constraint_exact; + + PED_ASSERT(disk != NULL); + PED_ASSERT(part != NULL); + PED_ASSERT(ped_partition_is_active (part)); + + old_geom = part->geom; + if (!ped_disk_maximize_partition (disk, part, constraint)) + return NULL; + max_geom = ped_geometry_duplicate (&part->geom); + + constraint_exact = ped_constraint_exact (&old_geom); + ped_disk_set_partition_geom (disk, part, constraint_exact, + old_geom.start, old_geom.end); + ped_constraint_destroy (constraint_exact); + + /* this assertion should never fail, because the old + * geometry was valid + */ + PED_ASSERT (ped_geometry_test_equal (&part->geom, &old_geom)); + + return max_geom; +} + +/** + * Reduce the size of the extended partition to a minimum while still wrapping + * its logical partitions. If there are no logical partitions, remove the + * extended partition. + * + * \return 0 on failure. + */ +int +ped_disk_minimize_extended_partition (PedDisk* disk) +{ + PedPartition* first_logical; + PedPartition* last_logical; + PedPartition* walk; + PedPartition* ext_part; + PedConstraint* constraint; + int status; + + PED_ASSERT (disk != NULL); + + ext_part = ped_disk_extended_partition (disk); + if (!ext_part) + return 1; + + if (!_disk_push_update_mode (disk)) + return 0; + + first_logical = ext_part->part_list; + if (!first_logical) { + if (!_disk_pop_update_mode (disk)) + return 0; + return ped_disk_delete_partition (disk, ext_part); + } + + for (walk = first_logical; walk->next; walk = walk->next); + last_logical = walk; + + constraint = ped_constraint_any (disk->dev); + status = ped_disk_set_partition_geom (disk, ext_part, constraint, + first_logical->geom.start, + last_logical->geom.end); + ped_constraint_destroy (constraint); + + if (!_disk_pop_update_mode (disk)) + return 0; + return status; +} + +/** + * @} + */ + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** + * Returns a name that seems mildly appropriate for a partition type \p type. + * + * Eg, if you pass (PED_PARTITION_LOGICAL & PED_PARTITION_FREESPACE), it + * will return "free". This isn't to be taken too seriously - it's just + * useful for user interfaces, so you can show the user something ;-) + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + * + */ +const char* +ped_partition_type_get_name (PedPartitionType type) +{ + if (type & PED_PARTITION_METADATA) + return N_("metadata"); + else if (type & PED_PARTITION_FREESPACE) + return N_("free"); + else if (type & PED_PARTITION_EXTENDED) + return N_("extended"); + else if (type & PED_PARTITION_LOGICAL) + return N_("logical"); + else + return N_("primary"); +} + + +/** + * Returns a name for a \p flag, e.g. PED_PARTITION_BOOT will return "boot". + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + */ +const char* +ped_partition_flag_get_name (PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + return N_("boot"); + case PED_PARTITION_BIOS_GRUB: + return N_("bios_grub"); + case PED_PARTITION_ROOT: + return N_("root"); + case PED_PARTITION_SWAP: + return N_("swap"); + case PED_PARTITION_HIDDEN: + return N_("hidden"); + case PED_PARTITION_RAID: + return N_("raid"); + case PED_PARTITION_LVM: + return N_("lvm"); + case PED_PARTITION_LBA: + return N_("lba"); + case PED_PARTITION_HPSERVICE: + return N_("hp-service"); + case PED_PARTITION_PALO: + return N_("palo"); + case PED_PARTITION_PREP: + return N_("prep"); + case PED_PARTITION_MSFT_RESERVED: + return N_("msftres"); + case PED_PARTITION_APPLE_TV_RECOVERY: + return N_("atvrecv"); + case PED_PARTITION_DIAG: + return N_("diag"); + case PED_PARTITION_LEGACY_BOOT: + return N_("legacy_boot"); + + default: + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unknown partition flag, %d."), + flag); + return NULL; + } +} + +/** + * Iterates through all flags. + * + * ped_partition_flag_next(0) returns the first flag + * + * \return the next flag, or 0 if there are no more flags + */ +PedPartitionFlag +ped_partition_flag_next (PedPartitionFlag flag) +{ + return (flag + 1) % (PED_PARTITION_LAST_FLAG + 1); +} + +/** + * Returns the flag associated with \p name. + * + * \p name can be the English + * string, or the translation for the native language. + */ +PedPartitionFlag +ped_partition_flag_get_by_name (const char* name) +{ + PedPartitionFlag flag; + const char* flag_name; + + for (flag = ped_partition_flag_next (0); flag; + flag = ped_partition_flag_next (flag)) { + flag_name = ped_partition_flag_get_name (flag); + if (strcasecmp (name, flag_name) == 0 + || strcasecmp (name, _(flag_name)) == 0) + return flag; + } + + return 0; +} + +static void +ped_partition_print (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + printf (" %-10s %02d (%d->%d)\n", + ped_partition_type_get_name (part->type), + part->num, + (int) part->geom.start, (int) part->geom.end); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +/** + * Prints a summary of disk's partitions. Useful for debugging. + */ +void +ped_disk_print (const PedDisk* disk) +{ + PedPartition* part; + + PED_ASSERT (disk != NULL); + + for (part = disk->part_list; part; + part = ped_disk_next_partition (disk, part)) + ped_partition_print (part); +} + +/** @} */ diff --git a/libparted/exception.c b/libparted/exception.c new file mode 100644 index 0000000..910d25c --- /dev/null +++ b/libparted/exception.c @@ -0,0 +1,311 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file exception.c */ + +/** + * \addtogroup PedException + * + * \brief Exception handling. + * + * There are a few types of exceptions: PED_EXCEPTION_INFORMATION, + * PED_EXCEPTION_WARNING, PED_EXCEPTION_ERROR, PED_EXCEPTION_FATAL, + * PED_EXCEPTION_BUG. + * + * They are "thrown" when one of the above events occur while executing + * a libparted function. For example, if ped_device_open() fails + * because the device doesn't exist, an exception will be thrown. + * Exceptions contain text describing what the event was. It will give + * at least one option for resolving the exception: PED_EXCEPTION_FIX, + * PED_EXCEPTION_YES, PED_EXCEPTION_NO, PED_EXCEPTION_OK, PED_EXCEPTION_RETRY, + * PED_EXCEPTION_IGNORE, PED_EXCEPTION_CANCEL. After an exception is thrown, + * there are two things that can happen: + * + * -# an exception handler is called, which selects how the exception should be + * resolved (usually by asking the user). Also note: an exception handler may + * choose to return PED_EXCEPTION_UNHANDLED. In this case, a default action + * will be taken by libparted (respectively the code that threw the + * exception). In general, a default action will be "safe". + * -# the exception is not handled, because the caller of the function wants to + * handle everything itself. In this case, PED_EXCEPTION_UNHANDLED is + * returned. + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#define N_(String) String +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +int ped_exception = 0; + +static PedExceptionOption default_handler (PedException* ex); + +static PedExceptionHandler* ex_handler = default_handler; +static PedException* ex = NULL; +static int ex_fetch_count = 0; + +static const char *const type_strings [] = { + N_("Information"), + N_("Warning"), + N_("Error"), + N_("Fatal"), + N_("Bug"), + N_("No Implementation") +}; + +static const char *const option_strings [] = { + N_("Fix"), + N_("Yes"), + N_("No"), + N_("OK"), + N_("Retry"), + N_("Ignore"), + N_("Cancel") +}; + +/** + * Return a string describing an exception type. + */ +char* +ped_exception_get_type_string (PedExceptionType ex_type) +{ + return (char *) type_strings [ex_type - 1]; +} + +/* FIXME: move this out to the prospective math.c */ +/* FIXME: this can probably be done more efficiently */ +static int +ped_log2 (int n) +{ + int x; + + PED_ASSERT (n > 0); + + for (x=0; 1 << x <= n; x++); + + return x - 1; +} + +/** + * Return a string describing an exception option. + */ +char* +ped_exception_get_option_string (PedExceptionOption ex_opt) +{ + return (char *) option_strings [ped_log2 (ex_opt)]; +} + +static PedExceptionOption +default_handler (PedException* e) +{ + if (e->type == PED_EXCEPTION_BUG) + fprintf (stderr, + _("A bug has been detected in GNU Parted. " + "Refer to the web site of parted " + "http://www.gnu.org/software/parted/parted.html " + "for more information of what could be useful " + "for bug submitting! " + "Please email a bug report to " + "%s containing at least the " + "version (%s) and the following message: "), + PACKAGE_BUGREPORT, VERSION); + else + fprintf (stderr, "%s: ", + ped_exception_get_type_string (e->type)); + fprintf (stderr, "%s\n", e->message); + + switch (e->options) { + case PED_EXCEPTION_OK: + case PED_EXCEPTION_CANCEL: + case PED_EXCEPTION_IGNORE: + return e->options; + + default: + return PED_EXCEPTION_UNHANDLED; + } +} + +/** + * Set the exception handler. + * + * The exception handler should return ONE of the options set in ex->options, + * indicating the way the event should be resolved. + */ +void +ped_exception_set_handler (PedExceptionHandler* handler) +{ + if (handler) + ex_handler = handler; + else + ex_handler = default_handler; +} + +/** + * Get the current exception handler. + */ +PedExceptionHandler * +ped_exception_get_handler (void) +{ + if (ex_handler) + return ex_handler; + return default_handler; +} + +/** + * Assert that the current exception has been resolved. + */ +void +ped_exception_catch () +{ + if (ped_exception) { + ped_exception = 0; + free (ex->message); + free (ex); + ex = NULL; + } +} + +static PedExceptionOption +do_throw () +{ + PedExceptionOption ex_opt; + + ped_exception = 1; + + if (ex_fetch_count) { + return PED_EXCEPTION_UNHANDLED; + } else { + ex_opt = ex_handler (ex); + ped_exception_catch (); + return ex_opt; + } +} + +/** + * Throw an exception. + * + * You can also use this in a program using libparted. + * "message" is a printf-like format string, so you can do + * + * \code + * ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_CANCEL, + * "Can't open %s", file_name); + * \endcode + * + * Returns the option selected to resolve the exception. If the exception was + * unhandled, PED_EXCEPTION_UNHANDLED is returned. + */ +PedExceptionOption +ped_exception_throw (PedExceptionType ex_type, + PedExceptionOption ex_opts, const char* message, ...) +{ + va_list arg_list; + int result; + static int size = 1000; + + if (ex) + ped_exception_catch (); + + ex = (PedException*) malloc (sizeof (PedException)); + if (!ex) + goto no_memory; + + ex->type = ex_type; + ex->options = ex_opts; + + while (message) { + ex->message = (char*) malloc (size * sizeof (char)); + if (!ex->message) + goto no_memory; + + va_start (arg_list, message); + result = vsnprintf (ex->message, size, message, arg_list); + va_end (arg_list); + + if (result > -1 && result < size) + break; + + size += 10; + free (ex->message); + } + + return do_throw (); + +no_memory: + fputs ("Out of memory in exception handler!\n", stderr); + + va_start (arg_list, message); + vfprintf (stderr, message, arg_list); + va_end (arg_list); + + return PED_EXCEPTION_UNHANDLED; +} + +/** + * Rethrow an unhandled exception. + * This means repeating the last ped_exception_throw() statement. + */ +PedExceptionOption +ped_exception_rethrow () +{ + return do_throw (); +} + +/** + * Indicates that exceptions should not go to the exception handler, but + * passed up to the calling function(s). All calls to + * ped_exception_throw() will return PED_EXCEPTION_UNHANDLED. + */ +void +ped_exception_fetch_all () +{ + ex_fetch_count++; +} + +/** + * Indicates that the calling function does not want to accept any + * responsibility for exceptions any more. + * + * \note a caller of that function may still want responsibility, so + * ped_exception_throw() may not invoke the exception handler. + * + * \warning every call to this function must have a preceding + * ped_exception_fetch_all(). + */ +void +ped_exception_leave_all () +{ + PED_ASSERT (ex_fetch_count > 0); + ex_fetch_count--; +} + +/** @} */ diff --git a/libparted/filesys.c b/libparted/filesys.c new file mode 100644 index 0000000..bf458e5 --- /dev/null +++ b/libparted/filesys.c @@ -0,0 +1,861 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2001, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file filesys.c */ + +/** + * \addtogroup PedFileSystem + * + * \note File systems exist on a PedGeometry - NOT a PedPartition. + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define BUFFER_SIZE 4096 /* in sectors */ + +static PedFileSystemType* fs_types = NULL; +static PedFileSystemAlias* fs_aliases = NULL; + +void +ped_file_system_type_register (PedFileSystemType* fs_type) +{ + PED_ASSERT (fs_type != NULL); + PED_ASSERT (fs_type->ops != NULL); + PED_ASSERT (fs_type->name != NULL); + + fs_type->next = fs_types; + fs_types = fs_type; +} + +void +ped_file_system_type_unregister (PedFileSystemType* fs_type) +{ + PedFileSystemType* walk; + PedFileSystemType* last = NULL; + + PED_ASSERT (fs_types != NULL); + PED_ASSERT (fs_type != NULL); + + for (walk = fs_types; walk && walk != fs_type; + last = walk, walk = walk->next); + + PED_ASSERT (walk != NULL); + if (last) + ((struct _PedFileSystemType*) last)->next = fs_type->next; + else + fs_types = fs_type->next; +} + +void +ped_file_system_alias_register (PedFileSystemType* fs_type, const char* alias, + int deprecated) +{ + PedFileSystemAlias* fs_alias; + + PED_ASSERT (fs_type != NULL); + PED_ASSERT (alias != NULL); + + fs_alias = ped_malloc (sizeof *fs_alias); + if (!fs_alias) + return; + + fs_alias->next = fs_aliases; + fs_alias->fs_type = fs_type; + fs_alias->alias = alias; + fs_alias->deprecated = deprecated; + fs_aliases = fs_alias; +} + +void +ped_file_system_alias_unregister (PedFileSystemType* fs_type, + const char* alias) +{ + PedFileSystemAlias* walk; + PedFileSystemAlias* last = NULL; + + PED_ASSERT (fs_aliases != NULL); + PED_ASSERT (fs_type != NULL); + PED_ASSERT (alias != NULL); + + for (walk = fs_aliases; walk; last = walk, walk = walk->next) { + if (walk->fs_type == fs_type && !strcmp (walk->alias, alias)) + break; + } + + PED_ASSERT (walk != NULL); + if (last) + last->next = walk->next; + else + fs_aliases = walk->next; + free (walk); +} + +/** + * Get a PedFileSystemType by its @p name. + * + * @return @c NULL if none found. + */ +PedFileSystemType* +ped_file_system_type_get (const char* name) +{ + PedFileSystemType* walk; + PedFileSystemAlias* alias_walk; + + PED_ASSERT (name != NULL); + + for (walk = fs_types; walk != NULL; walk = walk->next) { + if (!strcasecmp (walk->name, name)) + break; + } + if (walk != NULL) + return walk; + + for (alias_walk = fs_aliases; alias_walk != NULL; + alias_walk = alias_walk->next) { + if (!strcasecmp (alias_walk->alias, name)) + break; + } + if (alias_walk != NULL) { + if (alias_walk->deprecated) + PED_DEBUG (0, "File system alias %s is deprecated", + name); + return alias_walk->fs_type; + } + + return NULL; +} + +/** + * Get the next PedFileSystemType after @p fs_type. + * + * @return @c NULL if @p fs_type is the last item in the list. + */ +PedFileSystemType* +ped_file_system_type_get_next (const PedFileSystemType* fs_type) +{ + if (fs_type) + return fs_type->next; + else + return fs_types; +} + +/** + * Get the next PedFileSystemAlias after @p fs_alias. + * + * @return @c NULL if @p fs_alias is the last item in the list. + */ +PedFileSystemAlias* +ped_file_system_alias_get_next (const PedFileSystemAlias* fs_alias) +{ + if (fs_alias) + return fs_alias->next; + else + return fs_aliases; +} + +/** + * Attempt to find a file system and return the region it occupies. + * + * @param fs_type The file system type to probe for. + * @param geom The region to be searched. + * + * @return @p NULL if @p fs_type file system wasn't detected + */ +PedGeometry* +ped_file_system_probe_specific ( + const PedFileSystemType* fs_type, PedGeometry* geom) +{ + PedGeometry* result; + + PED_ASSERT (fs_type != NULL); + PED_ASSERT (fs_type->ops->probe != NULL); + PED_ASSERT (geom != NULL); + + /* Fail all fs-specific probe-related tests when sector size + is not the default. */ + if (geom->dev->sector_size != PED_SECTOR_SIZE_DEFAULT) + return 0; + + if (!ped_device_open (geom->dev)) + return 0; + result = fs_type->ops->probe (geom); + ped_device_close (geom->dev); + return result; +} + +static int +_test_open (PedFileSystemType* fs_type, PedGeometry* geom) +{ + PedFileSystem* fs; + + ped_exception_fetch_all (); + fs = fs_type->ops->open (geom); + if (fs) + fs_type->ops->close (fs); + else + ped_exception_catch (); + ped_exception_leave_all (); + return fs != NULL; +} + +static PedFileSystemType* +_probe_with_open (PedGeometry* geom, int detected_count, + PedFileSystemType* detected[]) +{ + int i; + PedFileSystemType* open_detected = NULL; + + ped_device_open (geom->dev); + + /* If one and only one file system that Parted is able to open + * can be successfully opened on this geometry, return it. + * If more than one can be, return NULL. + */ + for (i=0; i<detected_count; i++) { + if (!detected[i]->ops->open || !_test_open (detected [i], geom)) + continue; + + if (open_detected) { + ped_device_close (geom->dev); + return NULL; + } else { + open_detected = detected [i]; + } + } + + /* If no file system has been successfully opened, and + * if Parted has detected at most one unopenable file system, + * return it. + */ + if (!open_detected) + for (i=0; i<detected_count; i++) { + if (detected[i]->ops->open) + continue; + if (open_detected) { + ped_device_close (geom->dev); + return NULL; + } else { + open_detected = detected [i]; + } + } + + ped_device_close (geom->dev); + return open_detected; +} + +static int +_geometry_error (const PedGeometry* a, const PedGeometry* b) +{ + PedSector start_delta = a->start - b->start; + PedSector end_delta = a->end - b->end; + + return abs (start_delta) + abs (end_delta); +} + +static PedFileSystemType* +_best_match (const PedGeometry* geom, PedFileSystemType* detected [], + const int detected_error [], int detected_count) +{ + int best_match = 0; + int i; + PedSector min_error; + + min_error = PED_MAX (4096, geom->length / 100); + + for (i = 1; i < detected_count; i++) { + if (detected_error [i] < detected_error [best_match]) + best_match = i; + } + + /* make sure the best match is significantly better than all the + * other matches + */ + for (i = 0; i < detected_count; i++) { + if (i == best_match) + continue; + + if (abs (detected_error [best_match] - detected_error [i]) + < min_error) + return NULL; + } + + return detected [best_match]; +} + + +/** + * Attempt to detect a file system in region \p geom. + * This function tries to be clever at dealing with ambiguous + * situations, such as when one file system was not completely erased before a + * new file system was created on top of it. + * + * \return a new PedFileSystem on success, \c NULL on failure + */ +PedFileSystemType* +ped_file_system_probe (PedGeometry* geom) +{ + PedFileSystemType* detected[32]; + int detected_error[32]; + int detected_count = 0; + PedFileSystemType* walk = NULL; + + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + return NULL; + + ped_exception_fetch_all (); + while ( (walk = ped_file_system_type_get_next (walk)) ) { + PedGeometry* probed; + + probed = ped_file_system_probe_specific (walk, geom); + if (probed) { + detected [detected_count] = walk; + detected_error [detected_count] + = _geometry_error (geom, probed); + detected_count++; + ped_geometry_destroy (probed); + } else { + ped_exception_catch (); + } + } + ped_exception_leave_all (); + + ped_device_close (geom->dev); + + if (!detected_count) + return NULL; + walk = _best_match (geom, detected, detected_error, detected_count); + if (walk) + return walk; + return _probe_with_open (geom, detected_count, detected); +} + +/** + * This function erases all file system signatures that indicate that a + * file system occupies a given region described by \p geom. + * After this operation ped_file_system_probe() won't detect any file system. + * + * \note ped_file_system_create() calls this before creating a new file system. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_file_system_clobber (PedGeometry* geom) +{ + PedFileSystemType* fs_type = NULL; + + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + goto error; + + ped_exception_fetch_all (); + while ((fs_type = ped_file_system_type_get_next (fs_type))) { + PedGeometry* probed; + + if (!fs_type->ops->clobber) + continue; + + probed = ped_file_system_probe_specific (fs_type, geom); + if (!probed) { + ped_exception_catch (); + continue; + } + ped_geometry_destroy (probed); + + if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) { + ped_exception_leave_all (); + goto error_close_dev; + } + } + ped_device_close (geom->dev); + ped_exception_leave_all (); + return 1; + +error_close_dev: + ped_device_close (geom->dev); +error: + return 0; +} + +/* This function erases all signatures that indicate the presence of + * a file system in a particular region, without erasing any data + * contained inside the "exclude" region. + */ +static int +ped_file_system_clobber_exclude (PedGeometry* geom, + const PedGeometry* exclude) +{ + PedGeometry* clobber_geom; + int status; + + if (ped_geometry_test_sector_inside (exclude, geom->start)) + return 1; + + clobber_geom = ped_geometry_duplicate (geom); + if (ped_geometry_test_overlap (clobber_geom, exclude)) + ped_geometry_set_end (clobber_geom, exclude->start - 1); + + status = ped_file_system_clobber (clobber_geom); + ped_geometry_destroy (clobber_geom); + return status; +} + +/** + * This function opens the file system stored on \p geom, if it + * can find one. + * It is often called in the following manner: + * \code + * fs = ped_file_system_open (&part.geom) + * \endcode + * + * \throws PED_EXCEPTION_ERROR if file system could not be detected + * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume + * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on + * \p geom is not implemented + * + * \return a PedFileSystem on success, \c NULL on failure. + */ +PedFileSystem* +ped_file_system_open (PedGeometry* geom) +{ + PedFileSystemType* type; + PedFileSystem* fs; + PedGeometry* probed_geom; + + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + goto error; + + type = ped_file_system_probe (geom); + if (!type) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Could not detect file system.")); + goto error_close_dev; + } + + probed_geom = ped_file_system_probe_specific (type, geom); + if (!probed_geom) + goto error_close_dev; + if (!ped_geometry_test_inside (geom, probed_geom)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The file system is bigger than its volume!")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_probed_geom; + } + + if (!type->ops->open) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for opening %s file systems " + "is not implemented yet."), + type->name); + goto error_destroy_probed_geom; + } + + fs = type->ops->open (probed_geom); + if (!fs) + goto error_destroy_probed_geom; + ped_geometry_destroy (probed_geom); + return fs; + +error_destroy_probed_geom: + ped_geometry_destroy (probed_geom); +error_close_dev: + ped_device_close (geom->dev); +error: + return 0; +} + +/** + * This function initializes a new file system of type \p type on + * a region described by \p geom, writing out appropriate metadata and + * signatures. If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type + * is not implemented yet + * + * \return a PedFileSystem on success, \c NULL on failure + */ +PedFileSystem* +ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type, + PedTimer* timer) +{ + PedFileSystem* fs; + + PED_ASSERT (geom != NULL); + PED_ASSERT (type != NULL); + + if (!type->ops->create) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for creating %s file systems " + "is not implemented yet."), + type->name); + goto error; + } + + if (!ped_device_open (geom->dev)) + goto error; + + if (!ped_file_system_clobber (geom)) + goto error_close_dev; + fs = type->ops->create (geom, timer); + if (!fs) + goto error_close_dev; + return fs; + +error_close_dev: + ped_device_close (geom->dev); +error: + return 0; +} + +/** + * Close file system \p fs. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_file_system_close (PedFileSystem* fs) +{ + PedDevice* dev = fs->geom->dev; + + PED_ASSERT (fs != NULL); + + if (!fs->type->ops->close (fs)) + goto error_close_dev; + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); + return 0; +} + +/** + * Check \p fs file system for errors. + * + * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is + * not implemented yet + * + * \return \c 0 on failure (i.e. unfixed errors) + */ +int +ped_file_system_check (PedFileSystem* fs, PedTimer* timer) +{ + PED_ASSERT (fs != NULL); + + if (!fs->type->ops->check) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for checking %s file systems " + "is not implemented yet."), + fs->type->name); + return 0; + } + return fs->type->ops->check (fs, timer); +} + +static int +_raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer) +{ + char* buf; + PedSector pos; + + PED_ASSERT (src != NULL); + PED_ASSERT (dest != NULL); + PED_ASSERT (src->length <= dest->length); + + buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */ + if (!buf) + goto error; + + if (!ped_device_open (src->dev)) + goto error_free_buf; + if (!ped_device_open (dest->dev)) + goto error_close_src; + + for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) { + ped_timer_update (timer, 1.0 * pos / src->length); + if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE)) + goto error_close_dest; + if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE)) + goto error_close_dest; + } + if (pos < src->length) { + ped_timer_update (timer, 1.0 * pos / src->length); + if (!ped_geometry_read (src, buf, pos, src->length - pos)) + goto error_close_dest; + if (!ped_geometry_write (dest, buf, pos, src->length - pos)) + goto error_close_dest; + } + ped_timer_update (timer, 1.0); + + ped_device_close (src->dev); + ped_device_close (dest->dev); + free (buf); + return 1; + +error_close_dest: + ped_device_close (dest->dev); +error_close_src: + ped_device_close (src->dev); +error_free_buf: + free (buf); +error: + return 0; +} + +static PedFileSystem* +_raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom, + PedTimer* timer) +{ + PedFileSystem* new_fs; + PedTimer* sub_timer = NULL; + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("raw block copying")); + + sub_timer = ped_timer_new_nested (timer, 0.95); + if (!_raw_copy (fs->geom, geom, sub_timer)) + goto error; + ped_timer_destroy_nested (sub_timer); + + new_fs = ped_file_system_open (geom); + if (!new_fs) + goto error; + + ped_timer_set_state_name (timer, _("growing file system")); + + sub_timer = ped_timer_new_nested (timer, 0.05); + if (!ped_file_system_resize (new_fs, geom, sub_timer)) + goto error_close_new_fs; + ped_timer_destroy_nested (sub_timer); + return new_fs; + +error_close_new_fs: + ped_file_system_close (new_fs); +error: + ped_timer_destroy_nested (sub_timer); + return NULL; +} + +/** + * Create a new file system (of the same type) on \p geom, and + * copy the contents of \p fs into the new filesystem. + * If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition + * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs + * is not implemented yet + * + * \return a new PedFileSystem on success, \c NULL on failure + */ +PedFileSystem* +ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* new_fs; + + PED_ASSERT (fs != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + goto error; + + if (ped_geometry_test_overlap (fs->geom, geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Can't copy onto an overlapping partition.")); + goto error_close_dev; + } + + if (!fs->checked && fs->type->ops->check) { + if (!ped_file_system_check (fs, timer)) + goto error_close_dev; + } + + if (!ped_file_system_clobber_exclude (geom, fs->geom)) + goto error_close_dev; + + if (!fs->type->ops->copy) { + if (fs->type->ops->resize) { + if (fs->geom->length <= geom->length) + return _raw_copy_and_resize ( + fs, (PedGeometry*) geom, + timer); + + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Direct support for copying file systems is " + "not yet implemented for %s. However, " + "support for resizing is implemented. " + "Therefore, the file system can be copied if " + "the new partition is at least as big as the " + "old one. So, either shrink the partition " + "you are trying to copy, or copy to a bigger " + "partition."), + fs->type->name); + goto error_close_dev; + } else { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for copying %s file systems is not " + "implemented yet."), + fs->type->name); + goto error_close_dev; + } + } + new_fs = fs->type->ops->copy (fs, geom, timer); + if (!new_fs) + goto error_close_dev; + return new_fs; + +error_close_dev: + ped_device_close (geom->dev); +error: + return NULL;; +} + +/** + * Resize \p fs to new geometry \p geom. + * + * \p geom should satisfy the ped_file_system_get_resize_constraint(). + * (This isn't asserted, so it's not a bug not to... just it's likely + * to fail ;) If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs + * is not implemented yet + * + * \return \c 0 on failure + */ +int +ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PED_ASSERT (fs != NULL); + PED_ASSERT (geom != NULL); + + if (!fs->type->ops->resize) { + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for resizing %s file systems " + "is not implemented yet."), + fs->type->name); + return 0; + } + if (!fs->checked && fs->type->ops->check) { + if (!ped_file_system_check (fs, timer)) + return 0; + } + if (!ped_file_system_clobber_exclude (geom, fs->geom)) + return 0; + + return fs->type->ops->resize (fs, geom, timer); +} + +/** + * This function returns a constraint on the region that all file systems + * of a particular type \p fs_type created on device \p dev with + * ped_file_system_create() must satisfy. For example, FAT16 file systems must + * be at least 32 megabytes. + * + * \return \c NULL on failure + */ +PedConstraint* +ped_file_system_get_create_constraint (const PedFileSystemType* fs_type, + const PedDevice* dev) +{ + PED_ASSERT (fs_type != NULL); + PED_ASSERT (dev != NULL); + + if (!fs_type->ops->get_create_constraint) + return NULL; + return fs_type->ops->get_create_constraint (dev); +} +/** + * Return a constraint, that represents all of the possible ways the + * file system \p fs can be resized with ped_file_system_resize(). + * This takes into account the amount of used space on + * the filesystem \p fs and the capabilities of the resize algorithm. + * Hints: + * -# if constraint->start_align->grain_size == 0, or + * constraint->start_geom->length == 1, then the start cannot be moved + * -# constraint->min_size is the minimum size you can resize the partition + * to. You might want to tell the user this ;-). + * + * \return a PedConstraint on success, \c NULL on failure + */ +PedConstraint* +ped_file_system_get_resize_constraint (const PedFileSystem* fs) +{ + PED_ASSERT (fs != NULL); + + if (!fs->type->ops->get_resize_constraint) + return NULL; + return fs->type->ops->get_resize_constraint (fs); +} + +/** + * Get the constraint on copying \p fs with ped_file_system_copy() + * to somewhere on \p dev. + * + * \return a PedConstraint on success, \c NULL on failure + */ +PedConstraint* +ped_file_system_get_copy_constraint (const PedFileSystem* fs, + const PedDevice* dev) +{ + PedGeometry full_dev; + + PED_ASSERT (fs != NULL); + PED_ASSERT (dev != NULL); + + if (fs->type->ops->get_copy_constraint) + return fs->type->ops->get_copy_constraint (fs, dev); + + if (fs->type->ops->resize) { + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + fs->geom->length, dev->length); + } + + return NULL; +} + +/** @} */ diff --git a/libparted/fs/fat/Makefile b/libparted/fs/fat/Makefile new file mode 100644 index 0000000..3d9d31b --- /dev/null +++ b/libparted/fs/fat/Makefile @@ -0,0 +1,9 @@ + +CFLAGS+= -I../include -Wall -O2 + +OBJS= bootsector.o calc.o clstdup.o context.o count.o fat.o fatio.o resize.o table.o traverse.o + +all: $(OBJS) + +clean: + rm -f $(OBJS) diff --git a/libparted/fs/fat/bootsector.c b/libparted/fs/fat/bootsector.c new file mode 100644 index 0000000..a5d69c3 --- /dev/null +++ b/libparted/fs/fat/bootsector.c @@ -0,0 +1,452 @@ +/* + libparted + Copyright (C) 1998-2000, 2002, 2004, 2007, 2009-2011 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "fat.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +/* Reads in the boot sector (superblock), and does a minimum of sanity + * checking. The goals are: + * - to detect fat file systems, even if they are damaged [i.e. not + * return an error / throw an exception] + * - to fail detection if there's not enough information for + * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero) + */ +int +fat_boot_sector_read (FatBootSector* bs, const PedGeometry *geom) +{ + PED_ASSERT (bs != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_geometry_read (geom, bs, 0, 1)) + return 0; + + if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid signature for a FAT " + "file system.")); + return 0; + } + + if (!bs->system_id[0]) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid signature for a FAT " + "file system.")); + return 0; + } + + if (!bs->sector_size + || PED_LE16_TO_CPU (bs->sector_size) % PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid sector size for a FAT " + "file system.")); + return 0; + } + + if (!bs->cluster_size) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid cluster size for a FAT " + "file system.")); + return 0; + } + + if (!bs->reserved) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of reserved " + "sectors for a FAT file system.")); + return 0; + } + + if (bs->fats < 1 || bs->fats > 4) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of FATs.")); + return 0; + } + + return 1; +} + +/* + Don't trust the FAT12, FAT16 or FAT32 label string. + */ +FatType +fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom) +{ + PedSector logical_sector_size; + PedSector first_cluster_sector; + FatCluster cluster_count; + + if (!PED_LE16_TO_CPU (bs->dir_entries)) + return FAT_TYPE_FAT32; + + logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + first_cluster_sector + = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size + + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size + + PED_LE16_TO_CPU (bs->dir_entries) + / (512 / sizeof (FatDirEntry)); + cluster_count = (geom->length - first_cluster_sector) + / bs->cluster_size / logical_sector_size; + if (cluster_count > MAX_FAT12_CLUSTERS) + return FAT_TYPE_FAT16; + else + return FAT_TYPE_FAT12; +} + +/* Analyses the boot sector, and sticks appropriate numbers in + fs->type_specific. + + Note: you need to subtract (2 * cluster_sectors) off cluster offset, + because the first cluster is number 2. (0 and 1 are not real clusters, + and referencing them is a bug) + */ +int +fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int fat_entry_size; + + PED_ASSERT (bs != NULL); + + if (PED_LE16_TO_CPU (bs->sector_size) != 512) { + if (ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_IGNORE_CANCEL, + _("This file system has a logical sector size of %d. " + "GNU Parted is known not to work properly with sector " + "sizes other than 512 bytes."), + (int) PED_LE16_TO_CPU (bs->sector_size)) + != PED_EXCEPTION_IGNORE) + return 0; + } + + fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track); + fs_info->heads = PED_LE16_TO_CPU (bs->heads); + if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63 + || fs_info->heads < 1 || fs_info->heads > 255) { + PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom; + int cyl_count = 0; + + if (fs_info->heads > 0 && fs_info->sectors_per_track > 0) + cyl_count = fs->geom->dev->length / fs_info->heads + / fs_info->sectors_per_track; + + switch (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX + PED_EXCEPTION_IGNORE + + PED_EXCEPTION_CANCEL, + _("The file system's CHS geometry is (%d, %d, %d), " + "which is invalid. The partition table's CHS " + "geometry is (%d, %d, %d). If you select Ignore, " + "the file system's CHS geometry will be left " + "unchanged. If you select Fix, the file system's " + "CHS geometry will be set to match the partition " + "table's CHS geometry."), + cyl_count, fs_info->heads, fs_info->sectors_per_track, + bios_geom->cylinders, bios_geom->heads, + bios_geom->sectors)) { + + case PED_EXCEPTION_FIX: + fs_info->sectors_per_track = bios_geom->sectors; + fs_info->heads = bios_geom->heads; + bs->secs_track + = PED_CPU_TO_LE16 (fs_info->sectors_per_track); + bs->heads = PED_CPU_TO_LE16 (fs_info->heads); + if (!fat_boot_sector_write (bs, fs)) + return 0; + break; + + case PED_EXCEPTION_CANCEL: + return 0; + + case PED_EXCEPTION_IGNORE: + break; + + default: + break; + } + } + + if (bs->sectors) + fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors) + * fs_info->logical_sector_size; + else + fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count) + * fs_info->logical_sector_size; + + fs_info->fat_table_count = bs->fats; + fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries); + fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved) + * fs_info->logical_sector_size; + fs_info->cluster_sectors = bs->cluster_size + * fs_info->logical_sector_size; + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + if (fs_info->logical_sector_size == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says logical sector size is 0. " + "This is weird. ")); + return 0; + } + if (fs_info->fat_table_count == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says there are no FAT tables. This " + "is weird. ")); + return 0; + } + if (fs_info->cluster_sectors == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says clusters are 0 sectors. This " + "is weird. ")); + return 0; + } + + fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom); + if (fs_info->fat_type == FAT_TYPE_FAT12) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("File system is FAT12, which is unsupported.")); + return 0; + } + if (fs_info->fat_type == FAT_TYPE_FAT16) { + fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat16.serial_number); + fs_info->root_cluster = 0; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->root_dir_sector_count + = fs_info->root_dir_entry_count * sizeof (FatDirEntry) + / (512 * fs_info->logical_sector_size); + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat32.serial_number); + fs_info->info_sector_offset + = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.info_sector) + * fs_info->logical_sector_size; + fs_info->boot_sector_backup_offset + = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.backup_sector) + * fs_info->logical_sector_size; + fs_info->root_cluster + = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster); + fs_info->root_dir_offset = 0; + fs_info->root_dir_sector_count = 0; + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + fs_info->cluster_count + = (fs_info->sector_count - fs_info->cluster_offset) + / fs_info->cluster_sectors; + + fat_entry_size = fat_table_entry_size (fs_info->fat_type); + if (fs_info->cluster_count + 2 + > fs_info->fat_sectors * 512 / fat_entry_size) + fs_info->cluster_count + = fs_info->fat_sectors * 512 / fat_entry_size - 2; + + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + return 1; +} + +#ifndef DISCOVER_ONLY +int +fat_boot_sector_set_boot_code (FatBootSector* bs) +{ + PED_ASSERT (bs != NULL); + + memset (bs, 0, 512); + memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3); + memcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE, FAT_BOOT_CODE_LENGTH); + return 1; +} + +int +fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (bs != NULL); + + memcpy (bs->system_id, "MSWIN4.1", 8); + bs->sector_size = PED_CPU_TO_LE16 (fs_info->logical_sector_size * 512); + bs->cluster_size = fs_info->cluster_sectors + / fs_info->logical_sector_size; + bs->reserved = PED_CPU_TO_LE16 (fs_info->fat_offset + / fs_info->logical_sector_size); + bs->fats = fs_info->fat_table_count; + + bs->dir_entries = (fs_info->fat_type == FAT_TYPE_FAT16) + ? PED_CPU_TO_LE16 (fs_info->root_dir_entry_count) + : 0; + + if (fs_info->sector_count / fs_info->logical_sector_size > 0xffff + || fs_info->fat_type == FAT_TYPE_FAT32) { + bs->sectors = 0; + bs->sector_count = PED_CPU_TO_LE32 (fs_info->sector_count + / fs_info->logical_sector_size); + } else { + bs->sectors = PED_CPU_TO_LE16 (fs_info->sector_count + / fs_info->logical_sector_size); + bs->sector_count = 0; + } + + bs->media = 0xf8; + + bs->secs_track = PED_CPU_TO_LE16 (fs_info->sectors_per_track); + bs->heads = PED_CPU_TO_LE16 (fs_info->heads); + bs->hidden = PED_CPU_TO_LE32 (fs->geom->start); + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + bs->fat_length = 0; + bs->u.fat32.fat_length = PED_CPU_TO_LE32 (fs_info->fat_sectors + / fs_info->logical_sector_size); + bs->u.fat32.flags = 0; /* FIXME: what the hell are these? */ + bs->u.fat32.version = 0; /* must be 0, for Win98 bootstrap */ + bs->u.fat32.root_dir_cluster + = PED_CPU_TO_LE32 (fs_info->root_cluster); + bs->u.fat32.info_sector + = PED_CPU_TO_LE16 (fs_info->info_sector_offset + / fs_info->logical_sector_size); + bs->u.fat32.backup_sector + = PED_CPU_TO_LE16 (fs_info->boot_sector_backup_offset + / fs_info->logical_sector_size); + + bs->u.fat32.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ + + memset (bs->u.fat32.empty_1, 0, 12); + + bs->u.fat32.ext_signature = 0x29; + bs->u.fat32.serial_number + = PED_CPU_TO_LE32 (fs_info->serial_number); + memcpy (bs->u.fat32.volume_name, "NO NAME ", 11); + memcpy (bs->u.fat32.fat_name, "FAT32 ", 8); + } else { + bs->fat_length + = PED_CPU_TO_LE16 (fs_info->fat_sectors + / fs_info->logical_sector_size); + + bs->u.fat16.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ + + bs->u.fat16.ext_signature = 0x29; + bs->u.fat16.serial_number + = PED_CPU_TO_LE32 (fs_info->serial_number); + memcpy (bs->u.fat16.volume_name, "NO NAME ", 11); + memcpy (bs->u.fat16.fat_name, "FAT16 ", 8); + } + + bs->boot_sign = PED_CPU_TO_LE16 (0xaa55); + + return 1; +} + +int +fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (bs != NULL); + + if (!ped_geometry_write (fs->geom, bs, 0, 1)) + return 0; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!ped_geometry_write (fs->geom, bs, + fs_info->boot_sector_backup_offset, 1)) + return 0; + } + return ped_geometry_sync (fs->geom); +} + +int +fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int status; + + PED_ASSERT (is != NULL); + + if (!ped_geometry_read (fs->geom, is, fs_info->info_sector_offset, 1)) + return 0; + + if (PED_LE32_TO_CPU (is->signature_2) != FAT32_INFO_MAGIC2) { + status = ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The information sector has the wrong " + "signature (%x). Select cancel for now, " + "and send in a bug report. If you're " + "desperate, it's probably safe to ignore."), + PED_LE32_TO_CPU (is->signature_2)); + if (status == PED_EXCEPTION_CANCEL) return 0; + } + return 1; +} + +int +fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (is != NULL); + + fat_table_count_stats (fs_info->fat); + + memset (is, 0, 512); + + is->signature_1 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC1); + is->signature_2 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC2); + is->free_clusters = PED_CPU_TO_LE32 (fs_info->fat->free_cluster_count); + is->next_cluster = PED_CPU_TO_LE32 (fs_info->fat->last_alloc); + is->signature_3 = PED_CPU_TO_LE16 (FAT32_INFO_MAGIC3); + + return 1; +} + +int +fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (is != NULL); + + if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1)) + return 0; + return ped_geometry_sync (fs->geom); +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/bootsector.h b/libparted/fs/fat/bootsector.h new file mode 100644 index 0000000..d049ed7 --- /dev/null +++ b/libparted/fs/fat/bootsector.h @@ -0,0 +1,131 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_FAT_BOOTSECTOR_H +#define PED_FAT_BOOTSECTOR_H + +typedef struct _FatBootSector FatBootSector; +typedef struct _FatInfoSector FatInfoSector; + +#include "fat.h" + +#define FAT32_INFO_MAGIC1 0x41615252 +#define FAT32_INFO_MAGIC2 0x61417272 +#define FAT32_INFO_MAGIC3 0xaa55 + +/* stolen from mkdosfs, by Dave Hudson */ + +#define FAT_BOOT_MESSAGE \ +"This partition does not have an operating system loader installed on it.\n\r"\ +"Press a key to reboot..." + +#define FAT_BOOT_JUMP "\xeb\x58\x90" /* jmp +5a */ + +#define FAT_BOOT_CODE "\x0e" /* push cs */ \ + "\x1f" /* pop ds */ \ + "\xbe\x74\x7e" /* mov si, offset message */ \ + /* write_msg_loop: */ \ + "\xac" /* lodsb */ \ + "\x22\xc0" /* and al, al */ \ + "\x74\x06" /* jz done (+8) */ \ + "\xb4\x0e" /* mov ah, 0x0e */ \ + "\xcd\x10" /* int 0x10 */ \ + "\xeb\xf5" /* jmp write_msg_loop */ \ + /* done: */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x16" /* int 0x16 */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x19" /* int 0x19 */ \ + "\xeb\xfe" /* jmp +0 - in case int 0x19 */ \ + /* doesn't work */ \ + /* message: */ \ + FAT_BOOT_MESSAGE + +#define FAT_BOOT_CODE_LENGTH 128 + +struct __attribute__ ((packed)) _FatBootSector { + uint8_t boot_jump[3]; /* 00: Boot strap short or near jump */ + uint8_t system_id[8]; /* 03: system name */ + uint16_t sector_size; /* 0b: bytes per logical sector */ + uint8_t cluster_size; /* 0d: sectors/cluster */ + uint16_t reserved; /* 0e: reserved sectors */ + uint8_t fats; /* 10: number of FATs */ + uint16_t dir_entries; /* 11: number of root directory entries */ + uint16_t sectors; /* 13: if 0, total_sect supersedes */ + uint8_t media; /* 15: media code */ + uint16_t fat_length; /* 16: sectors/FAT for FAT12/16 */ + uint16_t secs_track; /* 18: sectors per track */ + uint16_t heads; /* 1a: number of heads */ + uint32_t hidden; /* 1c: hidden sectors (partition start) */ + uint32_t sector_count; /* 20: no. of sectors (if sectors == 0) */ + + union __attribute__ ((packed)) { + /* FAT16 fields */ + struct __attribute__ ((packed)) { + uint8_t drive_num; /* 24: */ + uint8_t empty_1; /* 25: */ + uint8_t ext_signature; /* 26: always 0x29 */ + uint32_t serial_number; /* 27: */ + uint8_t volume_name [11]; /* 2b: */ + uint8_t fat_name [8]; /* 36: */ + uint8_t boot_code[448]; /* 3f: Boot code (or message) */ + } fat16; + /* FAT32 fields */ + struct __attribute__ ((packed)) { + uint32_t fat_length; /* 24: size of FAT in sectors */ + uint16_t flags; /* 28: bit8: fat mirroring, low4: active fat */ + uint16_t version; /* 2a: minor * 256 + major */ + uint32_t root_dir_cluster; /* 2c: */ + uint16_t info_sector; /* 30: */ + uint16_t backup_sector; /* 32: */ + uint8_t empty_1 [12]; /* 34: */ + uint16_t drive_num; /* 40: */ + uint8_t ext_signature; /* 42: always 0x29 */ + uint32_t serial_number; /* 43: */ + uint8_t volume_name [11]; /* 47: */ + uint8_t fat_name [8]; /* 52: */ + uint8_t boot_code[420]; /* 5a: Boot code (or message) */ + } fat32; + } u; + + uint16_t boot_sign; /* 1fe: always 0xAA55 */ +}; + +struct __attribute__ ((packed)) _FatInfoSector { + uint32_t signature_1; /* should be 0x41615252 */ + uint8_t unused [480]; + uint32_t signature_2; /* should be 0x61417272 */ + uint32_t free_clusters; + uint32_t next_cluster; /* most recently allocated cluster */ + uint8_t unused2 [0xe]; + uint16_t signature_3; /* should be 0xaa55 */ +}; + +int fat_boot_sector_read (FatBootSector* bs, const PedGeometry* geom); +FatType fat_boot_sector_probe_type (const FatBootSector* bs, + const PedGeometry* geom); +int fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs); +int fat_boot_sector_set_boot_code (FatBootSector* bs); +int fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs); +int fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs); + +int fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs); +int fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs); +int fat_info_sector_write (const FatInfoSector* is, PedFileSystem* fs); + +#endif /* PED_FAT_BOOTSECTOR_H */ diff --git a/libparted/fs/fat/calc.c b/libparted/fs/fat/calc.c new file mode 100644 index 0000000..dddd84b --- /dev/null +++ b/libparted/fs/fat/calc.c @@ -0,0 +1,433 @@ +/* + libparted + Copyright (C) 1998-2000, 2002, 2007, 2009-2011 Free Software Foundation, + Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "fat.h" + +#ifndef DISCOVER_ONLY + +/* returns the minimum size of clusters for a given file system type */ +PedSector +fat_min_cluster_size (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; + case FAT_TYPE_FAT16: return 1024/512; + case FAT_TYPE_FAT32: return 4096/512; + } + return 0; +} + +static PedSector +_smallest_power2_over (PedSector ceiling) +{ + PedSector result = 1; + + while (result < ceiling) + result *= 2; + + return result; +} + +/* returns the minimum size of clusters for a given file system type */ +PedSector +fat_recommend_min_cluster_size (FatType fat_type, PedSector size) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; + case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type); + case FAT_TYPE_FAT32: + return PED_MAX(_smallest_power2_over(size + / MAX_FAT32_CLUSTERS), + fat_min_cluster_size (fat_type)); + } + return 0; +} + +/* returns the maxmimum size of clusters for a given file system type */ +PedSector +fat_max_cluster_size (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; /* dunno... who cares? */ + case FAT_TYPE_FAT16: return 65536/512; + case FAT_TYPE_FAT32: return 65536/512; + } + return 0; +} + +/* returns the minimum number of clusters for a given file system type */ +FatCluster +fat_min_cluster_count (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: + case FAT_TYPE_FAT16: + return fat_max_cluster_count (fat_type) / 2; + + case FAT_TYPE_FAT32: return 0xfff0; + } + return 0; +} + +/* returns the maximum number of clusters for a given file system type */ +FatCluster +fat_max_cluster_count (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 0xff0; + case FAT_TYPE_FAT16: return 0xfff0; + case FAT_TYPE_FAT32: return 0x0ffffff0; + } + return 0; +} + +/* what is this supposed to be? What drugs are M$ on? (Can I have some? :-) */ +PedSector +fat_min_reserved_sector_count (FatType fat_type) +{ + return (fat_type == FAT_TYPE_FAT32) ? 32 : 1; +} + +int +fat_check_resize_geometry (const PedFileSystem* fs, + const PedGeometry* geom, + PedSector new_cluster_sectors, + FatCluster new_cluster_count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector free_space; + PedSector min_free_space; + PedSector total_space; + PedSector new_total_space; + PedSector dir_space; + + PED_ASSERT (geom != NULL); + + dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors; + free_space = fs_info->fat->free_cluster_count + * fs_info->cluster_sectors; + total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors; + new_total_space = new_cluster_count * new_cluster_sectors; + min_free_space = total_space - new_total_space + dir_space; + + PED_ASSERT (new_cluster_count + <= fat_max_cluster_count (FAT_TYPE_FAT32)); + + if (free_space < min_free_space) { + char* needed = ped_unit_format (geom->dev, min_free_space); + char* have = ped_unit_format (geom->dev, free_space); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("You need %s of free disk space to shrink this " + "partition to this size. Currently, only %s is " + "free."), + needed, have); + free (needed); + free (have); + return 0; + } + + return 1; +} + + +/******************************************************************************/ + +/* DO NOT EDIT THIS ALGORITHM! + * As far as I can tell, this is the same algorithm used by Microsoft to + * calculate the size of the file allocaion tables, and the number of clusters. + * I have not verified this by dissassembling Microsoft code - I came to this + * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE). + * + * If you think this code makes no sense, then you are right. I will restrain + * the urge to inflict serious bodily harm on Microsoft people. + */ + +static int +entries_per_sector (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 512 * 3 / 2; + case FAT_TYPE_FAT16: + return 512 / 2; + case FAT_TYPE_FAT32: + return 512 / 4; + } + return 0; +} + +static int +calc_sizes (PedSector size, PedSector align, FatType fat_type, + PedSector root_dir_sectors, PedSector cluster_sectors, + FatCluster* out_cluster_count, PedSector* out_fat_size) +{ + PedSector data_fat_space; /* space available to clusters + FAT */ + PedSector fat_space; /* space taken by each FAT */ + PedSector cluster_space; /* space taken by clusters */ + FatCluster cluster_count; + int i; + + PED_ASSERT (out_cluster_count != NULL); + PED_ASSERT (out_fat_size != NULL); + + data_fat_space = size - fat_min_reserved_sector_count (fat_type) + - align; + if (fat_type == FAT_TYPE_FAT16) + data_fat_space -= root_dir_sectors; + + fat_space = 0; + for (i = 0; i < 2; i++) { + if (fat_type == FAT_TYPE_FAT32) + cluster_space = data_fat_space - fat_space; + else + cluster_space = data_fat_space - 2 * fat_space; + + cluster_count = cluster_space / cluster_sectors; + fat_space = ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type)); + } + + cluster_space = data_fat_space - 2 * fat_space; + cluster_count = cluster_space / cluster_sectors; + + /* looks like this should be part of the loop condition? + * Need to build the Big Table TM again to check + */ + if (fat_space < ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type))) { + fat_space = ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type)); + } + + if (cluster_count > fat_max_cluster_count (fat_type) + || cluster_count < fat_min_cluster_count (fat_type)) + return 0; + + *out_cluster_count = cluster_count; + *out_fat_size = fat_space; + + return 1; +} + +/****************************************************************************/ + +int +fat_calc_sizes (PedSector size, PedSector align, FatType fat_type, + PedSector root_dir_sectors, + PedSector* out_cluster_sectors, FatCluster* out_cluster_count, + PedSector* out_fat_size) +{ + PedSector cluster_sectors; + + PED_ASSERT (out_cluster_sectors != NULL); + PED_ASSERT (out_cluster_count != NULL); + PED_ASSERT (out_fat_size != NULL); + + for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size); + cluster_sectors <= fat_max_cluster_size (fat_type); + cluster_sectors *= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size); + cluster_sectors >= fat_min_cluster_size (fat_type); + cluster_sectors /= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + /* only make the cluster size really small (<4k) if a bigger one is + * isn't possible. Windows never makes FS's like this, but it + * seems to work... (do more tests!) + */ + for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + return 0; +} + +/* Same as fat_calc_sizes, except it only attempts to match a particular + * cluster size. This is useful, because the FAT resizer can only shrink the + * cluster size. + */ +int +fat_calc_resize_sizes ( + const PedGeometry* geom, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector cluster_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size) +{ + PED_ASSERT (geom != NULL); + PED_ASSERT (out_cluster_sectors != NULL); + PED_ASSERT (out_cluster_count != NULL); + PED_ASSERT (out_fat_size != NULL); + +/* libparted can only reduce the cluster size at this point */ + for (*out_cluster_sectors = cluster_sectors; + *out_cluster_sectors >= fat_min_cluster_size (fat_type); + *out_cluster_sectors /= 2) { + if (calc_sizes (geom->length, align, fat_type, root_dir_sectors, + *out_cluster_sectors, + out_cluster_count, out_fat_size)) + return 1; + } + return 0; +} + +/* Calculates the number of sectors needed to be added to cluster_offset, + to make the cluster on the new file system match up with the ones + on the old file system. + However, some space is reserved by fat_calc_resize_sizes() and + friends, to allow room for this space. If too much of this space is left + over, everyone will complain, so we have to be greedy, and use it all up... + */ +PedSector +fat_calc_align_sectors (const PedFileSystem* new_fs, + const PedFileSystem* old_fs) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + PedSector raw_old_meta_data_end; + PedSector new_meta_data_size; + PedSector min_new_meta_data_end; + PedSector new_data_size; + PedSector new_clusters_size; + PedSector align; + + new_meta_data_size + = fat_min_reserved_sector_count (new_fs_info->fat_type) + + new_fs_info->fat_sectors * 2; + + if (new_fs_info->fat_type == FAT_TYPE_FAT16) + new_meta_data_size += new_fs_info->root_dir_sector_count; + + raw_old_meta_data_end = old_fs->geom->start + + old_fs_info->cluster_offset; + + min_new_meta_data_end = new_fs->geom->start + new_meta_data_size; + + if (raw_old_meta_data_end > min_new_meta_data_end) + align = (raw_old_meta_data_end - min_new_meta_data_end) + % new_fs_info->cluster_sectors; + else + align = (new_fs_info->cluster_sectors + - ( (min_new_meta_data_end - raw_old_meta_data_end) + % new_fs_info->cluster_sectors )) + % new_fs_info->cluster_sectors; + + new_data_size = new_fs->geom->length - new_meta_data_size; + new_clusters_size = new_fs_info->cluster_count + * new_fs_info->cluster_sectors; + + while (new_clusters_size + align + new_fs_info->cluster_sectors + <= new_data_size) + align += new_fs_info->cluster_sectors; + + return align; +} + +int +fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + return sector >= fs_info->cluster_offset + && sector < fs_info->cluster_offset + + fs_info->cluster_sectors * fs_info->cluster_count; +} + +FatFragment +fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); + + return (cluster - 2) * fs_info->cluster_frags; +} + +FatCluster +fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return frag / fs_info->cluster_frags + 2; +} + +PedSector +fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return frag * fs_info->frag_sectors + fs_info->cluster_offset; +} + +FatFragment +fat_sector_to_frag (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (sector >= fs_info->cluster_offset); + + return (sector - fs_info->cluster_offset) / fs_info->frag_sectors; +} + +PedSector +fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); + + return (cluster - 2) * fs_info->cluster_sectors + + fs_info->cluster_offset; +} + +FatCluster +fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (sector >= fs_info->cluster_offset); + + return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors + + 2; +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/calc.h b/libparted/fs/fat/calc.h new file mode 100644 index 0000000..d5ab8a3 --- /dev/null +++ b/libparted/fs/fat/calc.h @@ -0,0 +1,76 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_FAT_CALC_H +#define PED_FAT_CALC_H + +extern PedSector fat_min_cluster_size (FatType fat_type); +extern PedSector fat_max_cluster_size (FatType fat_type); +extern FatCluster fat_min_cluster_count (FatType fat_type); +extern FatCluster fat_max_cluster_count (FatType fat_type); + +extern PedSector fat_min_reserved_sector_count (FatType fat_type); + +extern int fat_check_resize_geometry (const PedFileSystem* fs, + const PedGeometry* geom, + PedSector new_cluster_sectors, + FatCluster new_cluster_count); + +extern int fat_calc_sizes (PedSector size, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size); + +extern int fat_calc_resize_sizes (const PedGeometry* geom, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector cluster_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size); + +extern PedSector +fat_calc_align_sectors (const PedFileSystem* new_fs, + const PedFileSystem* old_fs); + +extern int +fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector); + +extern FatFragment +fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster); + +extern FatCluster +fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag); + +extern PedSector +fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag); + +extern FatFragment +fat_sector_to_frag (const PedFileSystem* fs, PedSector sector); + +extern PedSector +fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster); + +extern FatCluster +fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector); + +#endif /* PED_FAT_CALC_H */ diff --git a/libparted/fs/fat/clstdup.c b/libparted/fs/fat/clstdup.c new file mode 100644 index 0000000..3b8549e --- /dev/null +++ b/libparted/fs/fat/clstdup.c @@ -0,0 +1,422 @@ +/* + libparted + Copyright (C) 1998-2001, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <string.h> + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +static int +needs_duplicating (const FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatCluster cluster = fat_frag_to_cluster (ctx->old_fs, frag); + FatClusterFlag flag; + + PED_ASSERT (cluster >= 2 && cluster < old_fs_info->cluster_count + 2); + + flag = fat_get_fragment_flag (ctx->old_fs, frag); + switch (flag) { + case FAT_FLAG_FREE: + return 0; + + case FAT_FLAG_DIRECTORY: + return 1; + + case FAT_FLAG_FILE: + return fat_op_context_map_static_fragment (ctx, frag) == -1; + + case FAT_FLAG_BAD: + return 0; + } + + return 0; +} + +static int +search_next_fragment (FatOpContext* ctx) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + + for (; ctx->buffer_offset < fs_info->frag_count; ctx->buffer_offset++) { + if (needs_duplicating (ctx, ctx->buffer_offset)) + return 1; + } + return 0; /* all done! */ +} + +static int +read_marked_fragments (FatOpContext* ctx, FatFragment length) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + int status; + FatFragment i; + + ped_exception_fetch_all (); + status = fat_read_fragments (ctx->old_fs, fs_info->buffer, + ctx->buffer_offset, length); + ped_exception_leave_all (); + if (status) + return 1; + + ped_exception_catch (); + +/* something bad happened, so read fragments one by one. (The error may + have occurred on an unused fragment: who cares) */ + for (i = 0; i < length; i++) { + if (ctx->buffer_map [i]) { + if (!fat_read_fragment (ctx->old_fs, + fs_info->buffer + i * fs_info->frag_size, + ctx->buffer_offset + i)) + return 0; + } + } + + return 1; +} + +static int +fetch_fragments (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment fetch_length = 0; + FatFragment frag; + + for (frag = 0; frag < ctx->buffer_frags; frag++) + ctx->buffer_map [frag] = -1; + + for (frag = 0; + frag < ctx->buffer_frags + && ctx->buffer_offset + frag < old_fs_info->frag_count; + frag++) { + if (needs_duplicating (ctx, ctx->buffer_offset + frag)) { + ctx->buffer_map [frag] = 1; + fetch_length = frag + 1; + } + } + + if (!read_marked_fragments (ctx, fetch_length)) + return 0; + + return 1; +} + +/***************************************************************************** + * here starts the write code. All assumes that ctx->buffer_map [first] and + * ctx->buffer_map [last] are occupied by fragments that need to be duplicated. + *****************************************************************************/ + +/* finds the first fragment that is not going to get overwritten (that needs to + get read in) */ +static FatFragment +get_first_underlay (const FatOpContext* ctx, int first, int last) +{ + int old; + FatFragment new; + + PED_ASSERT (first <= last); + + new = ctx->buffer_map [first]; + for (old = first + 1; old <= last; old++) { + if (ctx->buffer_map [old] == -1) + continue; + new++; + if (ctx->buffer_map [old] != new) + return new; + } + return -1; +} + +/* finds the last fragment that is not going to get overwritten (that needs to + get read in) */ +static FatFragment +get_last_underlay (const FatOpContext* ctx, int first, int last) +{ + int old; + FatFragment new; + + PED_ASSERT (first <= last); + + new = ctx->buffer_map [last]; + for (old = last - 1; old >= first; old--) { + if (ctx->buffer_map [old] == -1) + continue; + new--; + if (ctx->buffer_map [old] != new) + return new; + } + return -1; +} + +/* "underlay" refers to the "static" fragments, that remain unchanged. + * when writing large chunks at a time, we don't want to clobber these, + * so we read them in, and write them back again. MUCH quicker that way. + */ +static int +quick_group_write_read_underlay (FatOpContext* ctx, int first, int last) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment first_underlay; + FatFragment last_underlay; + FatFragment underlay_length; + + PED_ASSERT (first <= last); + + first_underlay = get_first_underlay (ctx, first, last); + if (first_underlay == -1) + return 1; + last_underlay = get_last_underlay (ctx, first, last); + + PED_ASSERT (first_underlay <= last_underlay); + + underlay_length = last_underlay - first_underlay + 1; + if (!fat_read_fragments (ctx->new_fs, + new_fs_info->buffer + + (first_underlay - ctx->buffer_map [first]) + * new_fs_info->frag_size, + first_underlay, + underlay_length)) + return 0; + return 1; +} + +/* quick_group_write() makes no attempt to recover from errors - just + * does things fast. If there is an error, slow_group_write() is + * called. + * Note: we do syncing writes, to make sure there isn't any + * error writing out. It's rather difficult recovering from errors + * further on. + */ +static int +quick_group_write (FatOpContext* ctx, int first, int last) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int active_length; + int i; + int offset; + + PED_ASSERT (first <= last); + + ped_exception_fetch_all (); + if (!quick_group_write_read_underlay (ctx, first, last)) + goto error; + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + offset = ctx->buffer_map [i] - ctx->buffer_map [first]; + memcpy (new_fs_info->buffer + offset * new_fs_info->frag_size, + old_fs_info->buffer + i * new_fs_info->frag_size, + new_fs_info->frag_size); + } + + active_length = ctx->buffer_map [last] - ctx->buffer_map [first] + 1; + if (!fat_write_sync_fragments (ctx->new_fs, new_fs_info->buffer, + ctx->buffer_map [first], active_length)) + goto error; + + ped_exception_leave_all (); + return 1; + +error: + ped_exception_catch (); + ped_exception_leave_all (); + return 0; +} + +/* Writes fragments out, one at a time, avoiding errors on redundant writes + * on damaged parts of the disk we already know about. If there's an error + * on one of the required fragments, it gets marked as bad, and a replacement + * is found. + */ +static int +slow_group_write (FatOpContext* ctx, int first, int last) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int i; + + PED_ASSERT (first <= last); + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + while (!fat_write_sync_fragment (ctx->new_fs, + old_fs_info->buffer + i * old_fs_info->frag_size, + ctx->buffer_map [i])) { + fat_table_set_bad (new_fs_info->fat, + ctx->buffer_map [i]); + ctx->buffer_map [i] = fat_table_alloc_cluster + (new_fs_info->fat); + if (ctx->buffer_map [i] == 0) + return 0; + } + } + return 1; +} + +static int +update_remap (FatOpContext* ctx, int first, int last) +{ + int i; + + PED_ASSERT (first <= last); + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + ctx->remap [ctx->buffer_offset + i] = ctx->buffer_map [i]; + } + + return 1; +} + +static int +group_write (FatOpContext* ctx, int first, int last) +{ + PED_ASSERT (first <= last); + + if (!quick_group_write (ctx, first, last)) { + if (!slow_group_write (ctx, first, last)) + return 0; + } + if (!update_remap (ctx, first, last)) + return 0; + return 1; +} + +/* assumes fragment size and new_fs's cluster size are equal */ +static int +write_fragments (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int group_start; + int group_end = -1; /* shut gcc up! */ + FatFragment mapped_length; + FatFragment i; + FatCluster new_cluster; + + PED_ASSERT (ctx->buffer_offset < old_fs_info->frag_count); + + group_start = -1; + for (i = 0; i < ctx->buffer_frags; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + ctx->frags_duped++; + + new_cluster = fat_table_alloc_cluster (new_fs_info->fat); + if (!new_cluster) + return 0; + fat_table_set_eof (new_fs_info->fat, new_cluster); + ctx->buffer_map [i] = fat_cluster_to_frag (ctx->new_fs, + new_cluster); + + if (group_start == -1) + group_start = group_end = i; + + PED_ASSERT (ctx->buffer_map [i] + >= ctx->buffer_map [group_start]); + + mapped_length = ctx->buffer_map [i] + - ctx->buffer_map [group_start] + 1; + if (mapped_length <= ctx->buffer_frags) { + group_end = i; + } else { + /* ran out of room in the buffer, so write this group, + * and start a new one... + */ + if (!group_write (ctx, group_start, group_end)) + return 0; + group_start = group_end = i; + } + } + + PED_ASSERT (group_start != -1); + + if (!group_write (ctx, group_start, group_end)) + return 0; + return 1; +} + +/* default all fragments to unmoved + */ +static void +init_remap (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment i; + + for (i = 0; i < old_fs_info->frag_count; i++) + ctx->remap[i] = fat_op_context_map_static_fragment (ctx, i); +} + +static FatFragment +count_frags_to_dup (FatOpContext* ctx) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment i; + FatFragment total; + + total = 0; + + for (i = 0; i < fs_info->frag_count; i++) { + if (needs_duplicating (ctx, i)) + total++; + } + + return total; +} + +/* duplicates unreachable file clusters, and all directory clusters + */ +int +fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer) +{ + FatFragment total_frags_to_dup; + + init_remap (ctx); + total_frags_to_dup = count_frags_to_dup (ctx); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, "moving data"); + + ctx->buffer_offset = 0; + ctx->frags_duped = 0; + while (search_next_fragment (ctx)) { + ped_timer_update ( + timer, 1.0 * ctx->frags_duped / total_frags_to_dup); + + if (!fetch_fragments (ctx)) + return 0; + if (!write_fragments (ctx)) + return 0; + ctx->buffer_offset += ctx->buffer_frags; + } + + ped_timer_update (timer, 1.0); + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/clstdup.h b/libparted/fs/fat/clstdup.h new file mode 100644 index 0000000..77ef089 --- /dev/null +++ b/libparted/fs/fat/clstdup.h @@ -0,0 +1,27 @@ +/* + libparted + Copyright (C) 1999, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_FAT_CLSTDUP_H_INCLUDED +#define PED_FAT_CLSTDUP_H_INCLUDED + +#include "context.h" + +/* the big important one :-) */ +extern int fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer); + +#endif /* PED_FAT_CLSTDUP_H_INCLUDED */ diff --git a/libparted/fs/fat/context.c b/libparted/fs/fat/context.c new file mode 100644 index 0000000..4176b17 --- /dev/null +++ b/libparted/fs/fat/context.c @@ -0,0 +1,260 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <string.h> + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +/* Note: this deals with file system start and end sectors, even if the physical + * devices are different (eg for fat_copy()) Perhaps this is a hack, but it + * works ;-) + */ +static int +calc_deltas (FatOpContext* ctx) +{ + PedFileSystem* old_fs = ctx->old_fs; + PedFileSystem* new_fs = ctx->new_fs; + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + PedSector old_cluster_ofs; + PedSector new_cluster_ofs; + PedSector sector_delta; + + old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset; + new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset; + + if (new_cluster_ofs > old_cluster_ofs) { + ctx->start_move_dir = FAT_DIR_FORWARD; + sector_delta = new_cluster_ofs - old_cluster_ofs; + } else { + ctx->start_move_dir = FAT_DIR_BACKWARD; + sector_delta = old_cluster_ofs - new_cluster_ofs; + } + + if (sector_delta % new_fs_info->cluster_sectors) { + ped_exception_throw ( + PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("Cluster start delta = %d, which is not a multiple " + "of the cluster size %d."), + (int) sector_delta, + (int) new_fs_info->cluster_sectors); + return 0; + } + + ctx->start_move_delta = sector_delta / ctx->frag_sectors; + +#ifdef PED_VERBOSE + printf ("Start move delta is: %d %s.\n", + (int) ctx->start_move_delta, + (ctx->start_move_dir == FAT_DIR_FORWARD)? + "forwards" : "backwards"); +#endif + + return 1; +} + +FatOpContext* +fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + FatOpContext* ctx; + + ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext)); + if (!ctx) + goto error; + + ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors, + new_fs_info->cluster_sectors); + if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors)) + goto error; + if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors)) + goto error; + + ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors; + ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment) + * ctx->buffer_frags); + if (!ctx->buffer_map) + goto error_free_ctx; + + ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment) + * old_fs_info->frag_count); + if (!ctx->remap) + goto error_free_buffer_map; + + ctx->new_fs = new_fs; + ctx->old_fs = old_fs; + if (!calc_deltas (ctx)) + goto error_free_buffer_map; + + return ctx; + +error_free_buffer_map: + free (ctx->buffer_map); +error_free_ctx: + free (ctx); +error: + return NULL; +} + +void +fat_op_context_destroy (FatOpContext* ctx) +{ + free (ctx->buffer_map); + free (ctx->remap); + free (ctx); +} + +FatFragment +fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment result; + + if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev) + return -1; + + if (ctx->start_move_dir == FAT_DIR_FORWARD) { + if (frag < ctx->start_move_delta) + return -1; + result = frag - ctx->start_move_delta; + } else { + result = frag + ctx->start_move_delta; + } + + if (result >= new_fs_info->frag_count) + return -1; + + return result; +} + +FatCluster +fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst) +{ + FatFragment mapped_frag; + + mapped_frag = fat_op_context_map_static_fragment (ctx, + fat_cluster_to_frag (ctx->old_fs, clst)); + if (mapped_frag != -1) + return fat_frag_to_cluster (ctx->new_fs, mapped_frag); + else + return 0; +} + +FatFragment +fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag) +{ + return ctx->remap [frag]; +} + +FatCluster +fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst) +{ + FatFragment mapped_frag; + + mapped_frag = fat_op_context_map_fragment (ctx, + fat_cluster_to_frag (ctx->old_fs, clst)); + if (mapped_frag != -1) + return fat_frag_to_cluster (ctx->new_fs, mapped_frag); + else + return 0; +} + +/* This function sets the initial fat for the new resized file system. + This is in *NO WAY* a proper FAT table - all it does is: + a) mark bad clusters as bad. + b) mark used clusters (that is, clusters from the original FS that are + reachable from the resized one). Marks as EOF (i.e. used, end of + file chain). + c) mark original file system metadata as EOF (i.e. used), to prevent + it from being clobbered. This will leave the original file system + intact, until the partition table is modified, if the start of + the partition is moved. + + The FATs are rebuilt *properly* after cluster relocation. This here is + only to mark clusters as used, so when cluster relocation occurs, clusters + aren't relocated on top of ones marked in a, b or c. +*/ +int +fat_op_context_create_initial_fat (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster clst; + FatCluster new_clst; + PedSector sect; + PedSector new_sect; + FatFragment frag; + FatFragment new_frag; + FatClusterFlag frag_flag; + + new_fs_info->fat = fat_table_new ( + new_fs_info->fat_type, + new_fs_info->fat_sectors * 512 + / fat_table_entry_size (new_fs_info->fat_type)); + if (!new_fs_info->fat) + return 0; + + if (!fat_table_set_cluster_count (new_fs_info->fat, + new_fs_info->cluster_count)) + return 0; + +/* mark bad and used clusters */ + for (frag = 0; frag < old_fs_info->frag_count; frag++) { + frag_flag = fat_get_fragment_flag (ctx->old_fs, frag); + if (frag_flag == FAT_FLAG_FREE) + continue; + + new_frag = fat_op_context_map_static_fragment (ctx, frag); + if (new_frag == -1) + continue; + + new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag); + PED_ASSERT (new_clst != 0); + + if (frag_flag == FAT_FLAG_BAD) { + if (!fat_table_set_bad (new_fs_info->fat, new_clst)) + return 0; + } else { + if (!fat_table_set_eof (new_fs_info->fat, new_clst)) + return 0; + } + } + +/* mark metadata regions that map to clusters on the new FS */ + for (sect = 0; sect < old_fs_info->cluster_offset; sect++) { + new_sect = ped_geometry_map (ctx->new_fs->geom, + ctx->old_fs->geom, sect); + if (new_sect == -1 + || !fat_is_sector_in_clusters (ctx->new_fs, new_sect)) + continue; + + clst = fat_sector_to_cluster (ctx->new_fs, new_sect); + PED_ASSERT (clst != 0); + + if (!fat_table_set_eof (new_fs_info->fat, clst)) + return 0; + } + + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/context.h b/libparted/fs/fat/context.h new file mode 100644 index 0000000..704db90 --- /dev/null +++ b/libparted/fs/fat/context.h @@ -0,0 +1,69 @@ +/* + libparted + Copyright (C) 1999-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_FAT_CONTEXT_H_INCLUDED +#define PED_FAT_CONTEXT_H_INCLUDED + +#include "count.h" + +enum _FatDirection { + FAT_DIR_FORWARD, + FAT_DIR_BACKWARD +}; +typedef enum _FatDirection FatDirection; + +struct _FatOpContext { + PedFileSystem* old_fs; + PedFileSystem* new_fs; + + PedSector frag_sectors; /* should equal old_fs and + new_fs's frag_sectors */ + + FatDirection start_move_dir; + FatFragment start_move_delta; + + FatFragment buffer_offset; + FatFragment buffer_frags; + FatFragment* buffer_map; + + FatFragment frags_duped; + + FatFragment* remap; + + FatCluster new_root_dir [32]; +}; +typedef struct _FatOpContext FatOpContext; + +extern FatOpContext* fat_op_context_new (PedFileSystem* new_fs, + PedFileSystem* old_fs); + +extern void fat_op_context_destroy (FatOpContext* ctx); + +extern FatFragment fat_op_context_map_static_fragment (const FatOpContext* ctx, + FatFragment frag); +extern FatCluster fat_op_context_map_static_cluster (const FatOpContext* ctx, + FatCluster clst); + +extern FatFragment fat_op_context_map_fragment (const FatOpContext* ctx, + FatFragment frag); +extern FatCluster fat_op_context_map_cluster (const FatOpContext* ctx, + FatCluster clst); + +extern int fat_op_context_create_initial_fat (FatOpContext* ctx); + +#endif /* PED_FAT_CONTEXT_H_INCLUDED */ diff --git a/libparted/fs/fat/count.c b/libparted/fs/fat/count.c new file mode 100644 index 0000000..97ed2ab --- /dev/null +++ b/libparted/fs/fat/count.c @@ -0,0 +1,401 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "fat.h" +#include "traverse.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +#if 0 +/* extremely ugly hack: stick everything that obviously isn't an unmovable file + * in here. Note: DAT is a bit dubious. Unfortunately, it's used by the + * registry, so it'll be all over the place :-( + */ +static char* movable_extensions[] = { + "", + "1ST", + "AVI", + "BAK", "BAT", "BMP", + "CFG", "COM", "CSS", + "DAT", "DLL", "DOC", "DRV", + "EXE", + "FAQ", "FLT", "FON", + "GID", "GIF", + "HLP", "HTT", "HTM", + "ICO", "INI", + "JPG", + "LNK", "LOG", + "KBD", + "ME", "MID", "MSG", + "OCX", "OLD", + "PIF", "PNG", "PRV", + "RTF", + "SCR", "SYS", + "TMP", "TTF", "TXT", + "URL", + "WAV", + "VBX", "VOC", "VXD", + NULL +}; + +static char* +get_extension (char* file_name) +{ + char* ext; + + ext = strrchr (file_name, '.'); + if (!ext) + return ""; + if (strchr (ext, '\\')) + return ""; + return ext + 1; +} + +static int +is_movable_system_file (char* file_name) +{ + char* ext = get_extension (file_name); + int i; + + for (i = 0; movable_extensions [i]; i++) { + if (strcasecmp (ext, movable_extensions [i]) == 0) + return 1; + } + + return 0; +} +#endif /* 0 */ + +/* + prints out the sequence of clusters for a given file chain, beginning + at start_cluster. +*/ +#ifdef PED_VERBOSE +static void +print_chain (PedFileSystem* fs, FatCluster start) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster clst; + int this_row; + + this_row = 0; + for (clst = start; !fat_table_is_eof (fs_info->fat, clst); + clst = fat_table_get (fs_info->fat, clst)) { + printf (" %d", (int) clst); + if (++this_row == 7) { + putchar ('\n'); + this_row = 0; + } + } + putchar ('\n'); +} +#endif /* PED_VERBOSE */ + +static PedSector +remainder_round_up (PedSector a, PedSector b) +{ + PedSector result; + + result = a % b; + if (!result) + result = b; + return result; +} + +/* + traverse the FAT for a file/directory, marking each entry's flag + to "flag". +*/ +static int +flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start, + FatClusterFlag flag, PedSector size) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster clst; + FatCluster prev_clst; + int last_cluster_usage; + FatCluster chain_length = 0; + + if (fat_table_is_eof (fs_info->fat, start)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Bad directory entry for %s: first cluster is the " + "end of file marker."), + chain_name) + != PED_EXCEPTION_IGNORE) + return 0; + } + + for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst); + prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) { + chain_length++; + if (!clst) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: unterminated chain for %s. You " + "should run dosfsck or scandisk."), + chain_name); + return 0; + } + + if (clst >= fs_info->fat->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: cluster %d outside file system " + "in chain for %s. You should run dosfsck " + "or scandisk."), + (int) clst, chain_name); + return 0; + } + + if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: cluster %d is cross-linked for " + "%s. You should run dosfsck or scandisk."), + (int) clst, chain_name); + return 0; + } + + if (flag == FAT_FLAG_DIRECTORY) + fs_info->total_dir_clusters++; + + fs_info->cluster_info [clst].flag = flag; + fs_info->cluster_info [clst].units_used = 0; /* 0 == 64 */ + } + + if (size + && chain_length + != ped_div_round_up (size, fs_info->cluster_sectors)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("%s is %dk, but it has %d clusters (%dk)."), + chain_name, + (int) size / 2, + (int) chain_length, + (int) chain_length * fs_info->cluster_sectors / 2) + != PED_EXCEPTION_IGNORE) + return 0; + } + + last_cluster_usage + = ped_div_round_up (64 * remainder_round_up (size, + fs_info->cluster_sectors), + fs_info->cluster_sectors); + + fs_info->cluster_info [prev_clst].units_used = last_cluster_usage; + + return 1; +} + +/* + recursively traverses a directory, flagging all clusters in the process. + It frees the traverse_info structure before returning. +*/ +static int +flag_traverse_dir (FatTraverseInfo* trav_info) { + PedFileSystem* fs = trav_info->fs; + FatDirEntry* this_entry; + FatTraverseInfo* subdir_trav_info; + char file_name [512]; + char* file_name_start; + FatCluster first_cluster; + PedSector size; + + PED_ASSERT (trav_info != NULL); + + strcpy (file_name, trav_info->dir_name); + file_name_start = file_name + strlen (file_name); + + while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) { + if (fat_dir_entry_is_null_term (this_entry)) + break; + if (!fat_dir_entry_has_first_cluster (this_entry, fs)) + continue; + if (this_entry->name [0] == '.') + continue; /* skip . and .. entries */ + + fat_dir_entry_get_name (this_entry, file_name_start); + first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs); + size = ped_div_round_up (fat_dir_entry_get_length (this_entry), + 512); + +#ifdef PED_VERBOSE + printf ("%s: ", file_name); + print_chain (fs, first_cluster); +#endif + +#if 0 + if (fat_dir_entry_is_system_file (this_entry) + && !is_movable_system_file (file_name)) { + PedExceptionOption ex_status; + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The file %s is marked as a system file. " + "This means moving it could cause some " + "programs to stop working."), + file_name); + + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + return 0; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + } + } +#endif /* 0 */ + + if (fat_dir_entry_is_directory (this_entry)) { + if (!flag_traverse_fat (fs, file_name, first_cluster, + FAT_FLAG_DIRECTORY, size)) + return 0; + + subdir_trav_info = fat_traverse_directory (trav_info, + this_entry); + if (!subdir_trav_info) + return 0; + if (!flag_traverse_dir (subdir_trav_info)) + return 0; + } else if (fat_dir_entry_is_file (this_entry)) { + if (!flag_traverse_fat (fs, file_name, first_cluster, + FAT_FLAG_FILE, size)) + return 0; + } + } + + fat_traverse_complete (trav_info); + return 1; +} + +static void +_mark_bad_clusters (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster cluster; + + for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) { + if (fat_table_is_bad (fs_info->fat, cluster)) + fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD; + } +} + +/* + fills in cluster_info. Each FAT entry (= cluster) is flagged as either + FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY. + + Also, the fraction of each cluster (x/64) is recorded +*/ +int +fat_collect_cluster_info (PedFileSystem* fs) { + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTraverseInfo* trav_info; + + /* set all clusters to unused as a default */ + memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2); + fs_info->total_dir_clusters = 0; + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + trav_info = fat_traverse_begin (fs, fs_info->root_cluster, + "\\"); + if (!flag_traverse_dir (trav_info)) + return 0; + if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster, + FAT_FLAG_DIRECTORY, 0)) + return 0; + } else { + trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\"); + if (!flag_traverse_dir (trav_info)) + return 0; + } + + _mark_bad_clusters (fs); + return 1; +} + +FatClusterFlag +fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + return fs_info->cluster_info [cluster].flag; +} + +PedSector +fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int fraction; + + if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE) + return 0; + + fraction = fs_info->cluster_info [cluster].units_used; + if (fraction == 0) + fraction = 64; + + return fraction * fs_info->cluster_sectors / 64; +} + +FatClusterFlag +fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster cluster = fat_frag_to_cluster (fs, frag); + FatFragment offset = frag % fs_info->cluster_frags; + FatFragment last_frag_used; + FatClusterFlag flag; + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); + + flag = fat_get_cluster_flag (fs, cluster); + if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY) + return flag; + last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1) + / fs_info->frag_sectors; + if (offset > last_frag_used) + return FAT_FLAG_FREE; + else + return flag; +} + +int +fat_is_fragment_active (PedFileSystem* fs, FatFragment frag) +{ + switch (fat_get_fragment_flag (fs, frag)) { + case FAT_FLAG_FREE: + case FAT_FLAG_BAD: + return 0; + + case FAT_FLAG_FILE: + case FAT_FLAG_DIRECTORY: + return 1; + } + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/count.h b/libparted/fs/fat/count.h new file mode 100644 index 0000000..622e796 --- /dev/null +++ b/libparted/fs/fat/count.h @@ -0,0 +1,45 @@ +/* + libparted + Copyright (C) 1999-2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef COUNT_H_INCLUDED +#define COUNT_H_INCLUDED + +typedef enum _FatClusterFlag FatClusterFlag; +typedef struct _FatClusterInfo FatClusterInfo; + +enum _FatClusterFlag { + FAT_FLAG_FREE=0, + FAT_FLAG_FILE=1, + FAT_FLAG_DIRECTORY=2, + FAT_FLAG_BAD=3 +}; + +struct __attribute__ ((packed)) _FatClusterInfo { + unsigned int units_used:6; /* 1 unit = cluster_size / 64 */ + FatClusterFlag flag:2; +}; + +extern int fat_collect_cluster_info (PedFileSystem *fs); +extern FatClusterFlag fat_get_cluster_flag (PedFileSystem* fs, + FatCluster cluster); +extern PedSector fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster); +extern FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs, + FatFragment frag); +extern int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag); + +#endif /* COUNT_H_INCLUDED */ diff --git a/libparted/fs/fat/fat.c b/libparted/fs/fat/fat.c new file mode 100644 index 0000000..72e568e --- /dev/null +++ b/libparted/fs/fat/fat.c @@ -0,0 +1,888 @@ +/* + libparted + Copyright (C) 1998-2001, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <string.h> +#include <uuid/uuid.h> + +#include "fat.h" +#include "calc.h" + +PedFileSystem* +fat_alloc (const PedGeometry* geom) +{ + PedFileSystem* fs; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (FatSpecific*) ped_malloc (sizeof (FatSpecific)); + if (!fs->type_specific) + goto error_free_fs; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_type_specific; + + fs->checked = 0; + return fs; + +error_free_type_specific: + free (fs->type_specific); +error_free_fs: + free (fs); +error: + return NULL; +} + +/* Requires the boot sector to be analysed */ +int +fat_alloc_buffers (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + fs_info->buffer_sectors = BUFFER_SIZE; + fs_info->buffer = ped_malloc (fs_info->buffer_sectors * 512); + if (!fs_info->buffer) + goto error; + + fs_info->cluster_info = ped_malloc (fs_info->cluster_count + 2); + if (!fs_info->cluster_info) + goto error_free_buffer; + + return 1; + +error_free_buffer: + free (fs_info->buffer); +error: + return 0; +}; + +void +fat_free_buffers (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + free (fs_info->cluster_info); + free (fs_info->buffer); +} + +void +fat_free (PedFileSystem* fs) +{ + ped_geometry_destroy (fs->geom); + free (fs->type_specific); + free (fs); +} + +int +fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (fs_info->cluster_sectors % frag_sectors == 0 + && frag_sectors <= fs_info->cluster_sectors); + + fs_info->frag_size = frag_sectors * 512; + fs_info->frag_sectors = frag_sectors; + fs_info->buffer_frags = fs_info->buffer_sectors / frag_sectors; + fs_info->cluster_frags = fs_info->cluster_sectors / frag_sectors; + fs_info->frag_count = fs_info->cluster_count * fs_info->cluster_frags; + + return 1; +} + +PedGeometry* +fat_probe (PedGeometry* geom, FatType* fat_type) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + PedGeometry* result; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + if (!fat_boot_sector_read (&fs_info->boot_sector, geom)) + goto error_free_fs; + if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs)) + goto error_free_fs; + + *fat_type = fs_info->fat_type; + result = ped_geometry_new (geom->dev, geom->start, + fs_info->sector_count); + + fat_free (fs); + return result; + +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +PedGeometry* +fat_probe_fat16 (PedGeometry* geom) +{ + FatType fat_type; + PedGeometry* probed_geom = fat_probe (geom, &fat_type); + + if (probed_geom) { + if (fat_type == FAT_TYPE_FAT16) + return probed_geom; + ped_geometry_destroy (probed_geom); + } + return NULL; +} + +PedGeometry* +fat_probe_fat32 (PedGeometry* geom) +{ + FatType fat_type; + PedGeometry* probed_geom = fat_probe (geom, &fat_type); + + if (probed_geom) { + if (fat_type == FAT_TYPE_FAT32) + return probed_geom; + ped_geometry_destroy (probed_geom); + } + return NULL; +} + +#ifndef DISCOVER_ONLY +int +fat_clobber (PedGeometry* geom) +{ + FatBootSector boot_sector; + + if (!fat_boot_sector_read (&boot_sector, geom)) + return 1; + + boot_sector.system_id[0] = 0; + boot_sector.boot_sign = 0; + if (boot_sector.u.fat16.fat_name[0] == 'F') + boot_sector.u.fat16.fat_name[0] = 0; + if (boot_sector.u.fat32.fat_name[0] == 'F') + boot_sector.u.fat32.fat_name[0] = 0; + + return ped_geometry_write (geom, &boot_sector, 0, 1); +} + +static int +_init_fats (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster table_size; + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + fs_info->fat = fat_table_new (fs_info->fat_type, table_size); + if (!fs_info->fat) + goto error; + + if (!fat_table_read (fs_info->fat, fs, 0)) + goto error_free_fat; + + return 1; + +error_free_fat: + fat_table_destroy (fs_info->fat); +error: + return 0; +} + +PedFileSystem* +fat_open (PedGeometry* geom) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + if (!fat_boot_sector_read (&fs_info->boot_sector, geom)) + goto error_free_fs; + if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs)) + goto error_free_fs; + fs->type = (fs_info->fat_type == FAT_TYPE_FAT16) + ? &fat16_type + : &fat32_type; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!fat_info_sector_read (&fs_info->info_sector, fs)) + goto error_free_fs; + } + + if (!_init_fats (fs)) + goto error_free_fs; + if (!fat_alloc_buffers (fs)) + goto error_free_fat_table; + if (!fat_collect_cluster_info (fs)) + goto error_free_buffers; + + return fs; + +error_free_buffers: + fat_free_buffers (fs); +error_free_fat_table: + fat_table_destroy (fs_info->fat); +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +static int +fat_root_dir_clear (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + memset (fs_info->buffer, 0, 512 * fs_info->root_dir_sector_count); + return ped_geometry_write (fs->geom, fs_info->buffer, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count); +} + +/* hack: use the ext2 uuid library to generate a reasonably random (hopefully + * with /dev/random) number. Unfortunately, we can only use 4 bytes of it + */ +static uint32_t +_gen_new_serial_number (void) +{ + union { + uuid_t uuid; + uint32_t i; + } uu32; + + uuid_generate (uu32.uuid); + return uu32.i; +} + +PedFileSystem* +fat_create (PedGeometry* geom, FatType fat_type, PedTimer* timer) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + FatCluster table_size; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + fs_info->logical_sector_size = 1; + fs_info->sectors_per_track = geom->dev->bios_geom.sectors; + fs_info->heads = geom->dev->bios_geom.heads; + fs_info->sector_count = fs->geom->length; + fs_info->fat_table_count = 2; +/* some initial values, to be changed later */ + fs_info->root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT + / (512 / sizeof (FatDirEntry)); + fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT; + + fs_info->fat_type = fat_type; + if (!fat_calc_sizes (fs->geom->length, 0, + fs_info->fat_type, + fs_info->root_dir_sector_count, + &fs_info->cluster_sectors, + &fs_info->cluster_count, + &fs_info->fat_sectors)) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Partition too big/small for a %s file system."), + (fat_type == FAT_TYPE_FAT16) + ? fat16_type.name + : fat32_type.name); + goto error_free_fs; + } + + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + fs_info->fat_offset = fat_min_reserved_sector_count (fs_info->fat_type); + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + /* FAT16 */ + fs->type = &fat16_type; + + if (fs_info->cluster_count + > fat_max_cluster_count (fs_info->fat_type)) { + fs_info->cluster_count + = fat_max_cluster_count (fs_info->fat_type); + } + + fs_info->root_dir_sector_count + = FAT_ROOT_DIR_ENTRY_COUNT + / (512 / sizeof (FatDirEntry)); + fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } else { + /* FAT32 */ + fs->type = &fat32_type; + + fs_info->info_sector_offset = 1; + fs_info->boot_sector_backup_offset = 6; + + fs_info->root_dir_sector_count = 0; + fs_info->root_dir_entry_count = 0; + fs_info->root_dir_offset = 0; + + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + fs_info->fat = fat_table_new (fs_info->fat_type, table_size); + if (!fs_info->fat) + goto error_free_fs; + fat_table_set_cluster_count (fs_info->fat, fs_info->cluster_count); + if (!fat_alloc_buffers (fs)) + goto error_free_fat_table; + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->root_cluster + = fat_table_alloc_cluster (fs_info->fat); + fat_table_set_eof (fs_info->fat, fs_info->root_cluster); + memset (fs_info->buffer, 0, fs_info->cluster_size); + if (!fat_write_cluster (fs, fs_info->buffer, + fs_info->root_cluster)) + return 0; + } + + fs_info->serial_number = _gen_new_serial_number (); + + if (!fat_boot_sector_set_boot_code (&fs_info->boot_sector)) + goto error_free_buffers; + if (!fat_boot_sector_generate (&fs_info->boot_sector, fs)) + goto error_free_buffers; + if (!fat_boot_sector_write (&fs_info->boot_sector, fs)) + goto error_free_buffers; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!fat_info_sector_generate (&fs_info->info_sector, fs)) + goto error_free_buffers; + if (!fat_info_sector_write (&fs_info->info_sector, fs)) + goto error_free_buffers; + } + + if (!fat_table_write_all (fs_info->fat, fs)) + goto error_free_buffers; + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + if (!fat_root_dir_clear (fs)) + goto error_free_buffers; + } + + return fs; + +error_free_buffers: + fat_free_buffers (fs); +error_free_fat_table: + fat_table_destroy (fs_info->fat); +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +PedFileSystem* +fat_create_fat16 (PedGeometry* geom, PedTimer* timer) +{ + return fat_create (geom, FAT_TYPE_FAT16, timer); +} + +PedFileSystem* +fat_create_fat32 (PedGeometry* geom, PedTimer* timer) +{ + return fat_create (geom, FAT_TYPE_FAT32, timer); +} + +int +fat_close (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + fat_free_buffers (fs); + fat_table_destroy (fs_info->fat); + fat_free (fs); + return 1; +} + +/* Hack: just resize the file system outside of its boundaries! */ +PedFileSystem* +fat_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* new_fs; + + new_fs = ped_file_system_open (fs->geom); + if (!new_fs) + goto error; + if (!ped_file_system_resize (new_fs, geom, timer)) + goto error_close_new_fs; + return new_fs; + +error_close_new_fs: + ped_file_system_close (new_fs); +error: + return 0; +} + +static int +_compare_fats (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTable* table_copy; + FatCluster table_size; + int i; + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + + table_copy = fat_table_new (fs_info->fat_type, table_size); + if (!table_copy) + goto error; + + for (i = 1; i < fs_info->fat_table_count; i++) { + if (!fat_table_read (table_copy, fs, i)) + goto error_free_table_copy; + if (!fat_table_compare (fs_info->fat, table_copy)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The FATs don't match. If you don't know " + "what this means, then select cancel, run " + "scandisk on the file system, and then come " + "back.")) + != PED_EXCEPTION_IGNORE) + goto error_free_table_copy; + } + } + + fat_table_destroy (table_copy); + return 1; + +error_free_table_copy: + fat_table_destroy (table_copy); +error: + return 0; +} + +int +fat_check (PedFileSystem* fs, PedTimer* timer) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector cluster_sectors; + FatCluster cluster_count; + PedSector fat_sectors; + PedSector align_sectors; + FatCluster info_free_clusters; + + align_sectors = fs_info->fat_offset + - fat_min_reserved_sector_count (fs_info->fat_type); + + if (!fat_calc_sizes (fs->geom->length, + align_sectors, + fs_info->fat_type, + fs_info->root_dir_sector_count, + &cluster_sectors, + &cluster_count, + &fat_sectors)) { + if (ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_IGNORE_CANCEL, + _("There are no possible configurations for this FAT " + "type.")) + != PED_EXCEPTION_IGNORE) + goto error; + } + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + if (cluster_sectors != fs_info->cluster_sectors + || cluster_count != fs_info->cluster_count + || fat_sectors != fs_info->fat_sectors) { + if (ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("File system doesn't have expected sizes for " + "Windows to like it. " + "Cluster size is %dk (%dk expected); " + "number of clusters is %d (%d expected); " + "size of FATs is %d sectors (%d expected)."), + (int) fs_info->cluster_sectors / 2, + (int) cluster_sectors / 2, + (int) fs_info->cluster_count, + (int) cluster_count, + (int) fs_info->fat_sectors, + (int) fat_sectors) + != PED_EXCEPTION_IGNORE) + goto error; + } + } + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + info_free_clusters + = PED_LE32_TO_CPU (fs_info->info_sector.free_clusters); + if (info_free_clusters != (FatCluster) -1 + && info_free_clusters != fs_info->fat->free_cluster_count) { + if (ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("File system is reporting the free space as " + "%d clusters, not %d clusters."), + info_free_clusters, + fs_info->fat->free_cluster_count) + != PED_EXCEPTION_IGNORE) + goto error; + } + } + + if (!_compare_fats (fs)) + goto error; + + fs->checked = 1; + return 1; /* existence of fs implies consistency ;-) */ + +error: + return 0; +} + +/* Calculates how much space there will be in clusters in: + * old_fs intersect the-new-fs + */ +static PedSector +_calc_resize_data_size ( + const PedFileSystem* old_fs, + PedSector new_cluster_sectors, + FatCluster new_cluster_count, + PedSector new_fat_size) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + PedSector fat_size_delta; + + fat_size_delta = old_fs_info->fat_sectors - new_fat_size; + return new_cluster_sectors * new_cluster_count - fat_size_delta * 2; +} + +static int +_test_resize_size (const PedFileSystem* fs, + PedSector length, PedSector min_data_size) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedGeometry geom; + PedSector _cluster_sectors; + FatCluster _cluster_count; + PedSector _fat_size; + + ped_geometry_init (&geom, fs->geom->dev, fs->geom->start, length); + + if (fat_calc_resize_sizes ( + &geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT16, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &_cluster_sectors, + &_cluster_count, + &_fat_size) + && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count, + _fat_size) + >= min_data_size) + return 1; + + if (fat_calc_resize_sizes ( + &geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT32, + 0, + fs_info->cluster_sectors, + &_cluster_sectors, + &_cluster_count, + &_fat_size) + && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count, + _fat_size) + >= min_data_size) + return 1; + + return 0; +} + +/* does a binary search (!) for the mininum size. Too hard to compute directly + * (see calc_sizes() for why!) + */ +static PedSector +_get_min_resize_size (const PedFileSystem* fs, PedSector min_data_size) +{ + PedSector min_length = 0; + PedSector max_length = fs->geom->length; + PedSector length; + + while (min_length < max_length - 1) { + length = (min_length + max_length) / 2; + if (_test_resize_size (fs, length, min_data_size)) + max_length = length; + else + min_length = length; + } + +/* adds a bit of leeway (64 sectors), for resolving extra issues, like root + * directory allocation, that aren't covered here. + */ + return max_length + 64; +} + +PedConstraint* +fat_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedGeometry full_dev; + PedSector min_cluster_count; + FatCluster used_clusters; + PedSector min_data_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + used_clusters = fs_info->fat->cluster_count + - fs_info->fat->free_cluster_count; + min_cluster_count = used_clusters + fs_info->total_dir_clusters; + min_data_size = min_cluster_count * fs_info->cluster_sectors; + + return ped_constraint_new (ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + _get_min_resize_size (fs, min_data_size), + dev->length); +} + +PedConstraint* +fat_get_resize_constraint (const PedFileSystem* fs) +{ + return fat_get_copy_constraint (fs, fs->geom->dev); +} + +/* FIXME: fat_calc_sizes() needs to say "too big" or "too small", or + * something. This is a really difficult (maths) problem to do + * nicely... + * So, this algorithm works if dev->length / 2 is a valid fat_type + * size. (Which is how I got the magic numbers below) + */ +#if 0 +/* returns: -1 too small, 0 ok, 1 too big */ +static int +_test_create_size (PedSector length, FatType fat_type, + PedSector cluster_sectors, PedSector cluster_count) +{ + PedSector rootdir_sectors; + PedSector _cluster_sectors; + FatCluster _cluster_count; + PedSector _fat_size; + + rootdir_sectors = (fat_type == FAT_TYPE_FAT16) ? 16 : 0; + + if (!fat_calc_sizes (length, 0, fat_type, rootdir_sectors, + &_cluster_sectors, &_cluster_count, &_fat_size)) + return -1; // XXX: doesn't work... can't see a better way! + + if (_cluster_sectors < cluster_sectors) + return -1; + if (_cluster_sectors > cluster_sectors) + return 1; + + if (_cluster_count < cluster_count) + return -1; + if (_cluster_count > cluster_count) + return 1; + + return 0; +} + +static PedSector +_get_create_size (PedSector upper_bound, FatType fat_type, + PedSector cluster_sectors, FatCluster cluster_count) +{ + PedSector min_length = 0; + PedSector max_length = upper_bound; + PedSector length; + + while (1) { + length = (min_length + max_length) / 2; + switch (_test_create_size (length, fat_type, cluster_sectors, + cluster_count)) { + case -1: min_length = length; break; + case 0: return length; + case 1: max_length = length; break; + } + /* hack... won't always be able to get max cluster count + * with max cluster size, etc. */ + if (max_length - min_length == 1) + return min_length; + } + + return 0; /* shut gcc up */ +} +#endif + +PedConstraint* +fat_get_create_constraint_fat16 (const PedDevice* dev) +{ + PedGeometry full_dev; + PedSector min_size; + PedSector max_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + +#if 0 + min_size = _get_create_size (dev->length, FAT_TYPE_FAT16, + fat_min_cluster_size (FAT_TYPE_FAT16), + fat_min_cluster_count (FAT_TYPE_FAT16)); + max_size = _get_create_size (dev->length, FAT_TYPE_FAT16, + fat_max_cluster_size (FAT_TYPE_FAT16), + fat_max_cluster_count (FAT_TYPE_FAT16)); + if (!min_size) + return NULL; +#else + min_size = 65794; + max_size = 2097153; +#endif + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + min_size, max_size); +} + +PedConstraint* +fat_get_create_constraint_fat32 (const PedDevice* dev) +{ + PedGeometry full_dev; + PedSector min_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + +#if 0 + min_size = _get_create_size (dev->length, FAT_TYPE_FAT32, + fat_min_cluster_size (FAT_TYPE_FAT32), + fat_min_cluster_count (FAT_TYPE_FAT32)); + if (!min_size) + return NULL; +#else + min_size = 525224; +#endif + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + min_size, dev->length); +} +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps fat16_ops = { + probe: fat_probe_fat16, +#ifndef DISCOVER_ONLY + clobber: fat_clobber, + open: fat_open, + create: fat_create_fat16, + close: fat_close, + check: fat_check, + resize: fat_resize, + copy: fat_copy, + get_create_constraint: fat_get_create_constraint_fat16, + get_resize_constraint: fat_get_resize_constraint, + get_copy_constraint: fat_get_copy_constraint, +#else /* !DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* !DISCOVER_ONLY */ +}; + +static PedFileSystemOps fat32_ops = { + probe: fat_probe_fat32, +#ifndef DISCOVER_ONLY + clobber: fat_clobber, + open: fat_open, + create: fat_create_fat32, + close: fat_close, + check: fat_check, + resize: fat_resize, + copy: fat_copy, + get_create_constraint: fat_get_create_constraint_fat32, + get_resize_constraint: fat_get_resize_constraint, + get_copy_constraint: fat_get_copy_constraint, +#else /* !DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + resize: NULL, + copy: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* !DISCOVER_ONLY */ +}; + +#define FAT_BLOCK_SIZES ((int[2]){512, 0}) + +PedFileSystemType fat16_type = { + next: NULL, + ops: &fat16_ops, + name: "fat16", + block_sizes: FAT_BLOCK_SIZES +}; + +PedFileSystemType fat32_type = { + next: NULL, + ops: &fat32_ops, + name: "fat32", + block_sizes: FAT_BLOCK_SIZES +}; + +void +ped_file_system_fat_init () +{ + if (sizeof (FatBootSector) != 512) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("GNU Parted was miscompiled: the FAT boot sector " + "should be 512 bytes. FAT support will be disabled.")); + } else { + ped_file_system_type_register (&fat16_type); + ped_file_system_type_register (&fat32_type); + } +} + +void +ped_file_system_fat_done () +{ + ped_file_system_type_unregister (&fat16_type); + ped_file_system_type_unregister (&fat32_type); +} diff --git a/libparted/fs/fat/fat.h b/libparted/fs/fat/fat.h new file mode 100644 index 0000000..70355b6 --- /dev/null +++ b/libparted/fs/fat/fat.h @@ -0,0 +1,158 @@ +/* + libparted + Copyright (C) 1998-2001, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef FAT_H_INCLUDED +#define FAT_H_INCLUDED + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define BUFFER_SIZE 1024 /* buffer size in sectors (512 bytes) */ + +typedef uint32_t FatCluster; +typedef int32_t FatFragment; + +enum _FatType { + FAT_TYPE_FAT12, + FAT_TYPE_FAT16, + FAT_TYPE_FAT32 +}; +typedef enum _FatType FatType; + +typedef struct _FatSpecific FatSpecific; +typedef struct _FatDirEntry FatDirEntry; + +/* FIXME: YUCKY */ +#include "table.h" +#include "bootsector.h" +#include "context.h" +#include "fatio.h" +#include "traverse.h" +#include "calc.h" +#include "count.h" +#include "clstdup.h" + +struct __attribute__ ((packed)) _FatDirEntry { + char name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t is_upper_case_name; + uint8_t creation_time_low; /* milliseconds */ + uint16_t creation_time_high; + uint16_t creation_date; + uint16_t access_date; + uint16_t first_cluster_high; /* for FAT32 */ + uint16_t time; + uint16_t date; + uint16_t first_cluster; + uint32_t length; +}; + +struct _FatSpecific { + FatBootSector boot_sector; /* structure of boot sector */ + FatInfoSector info_sector; /* fat32-only information sector */ + + int logical_sector_size; /* illogical sector size :-) */ + PedSector sector_count; + + int sectors_per_track; /* BIOS CHS stuff (S) */ + int heads; /* BIOS CHS stuff (H) */ + + int cluster_size; + PedSector cluster_sectors; + FatCluster cluster_count; + int dir_entries_per_cluster; + + FatType fat_type; + int fat_table_count; + PedSector fat_sectors; + + uint32_t serial_number; + + PedSector info_sector_offset; /* FAT32 only */ + PedSector fat_offset; + PedSector root_dir_offset; /* non-FAT32 */ + PedSector cluster_offset; + PedSector boot_sector_backup_offset; + + FatCluster root_cluster; /* FAT32 only */ + int root_dir_entry_count; /* non-FAT32 */ + PedSector root_dir_sector_count; /* non-FAT32 */ + FatCluster total_dir_clusters; + + FatTable* fat; + FatClusterInfo* cluster_info; + + PedSector buffer_sectors; + char* buffer; + + int frag_size; + PedSector frag_sectors; + FatFragment frag_count; + FatFragment buffer_frags; + FatFragment cluster_frags; +}; + +#define FAT_SPECIFIC(fs) ((FatSpecific*) fs->type_specific) + +#define FAT_ROOT 0 + +#define DELETED_FLAG 0xe5 + +#define READONLY_ATTR 0x01 +#define HIDDEN_ATTR 0x02 +#define SYSTEM_ATTR 0x04 +#define VOLUME_LABEL_ATTR 0x08 +#define VFAT_ATTR 0x0f +#define DIRECTORY_ATTR 0x10 +#define ARCH_ATTR 0x20 + +#define MAX_FAT12_CLUSTERS 4086 +#define MAX_FAT16_CLUSTERS 65526 +#define MAX_FAT32_CLUSTERS 2000000 + +#define FAT_ROOT_DIR_ENTRY_COUNT 512 + +extern PedFileSystemType fat16_type; +extern PedFileSystemType fat32_type; + +extern void fat_print (const PedFileSystem* fs); + +extern PedFileSystem* fat_alloc (const PedGeometry* geom); +extern void fat_free (PedFileSystem* fs); +extern int fat_alloc_buffers (PedFileSystem* fs); +extern void fat_free_buffers (PedFileSystem* fs); + +extern int fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer); + +extern int fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors); + +#endif /* FAT_H_INCLUDED */ diff --git a/libparted/fs/fat/fatio.c b/libparted/fs/fat/fatio.c new file mode 100644 index 0000000..ecc2cd8 --- /dev/null +++ b/libparted/fs/fat/fatio.c @@ -0,0 +1,149 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "fat.h" +#include "fatio.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> + +#ifndef DISCOVER_ONLY + +int +fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_frag_to_sector (fs, frag); + PedSector sector_count = count * fs_info->frag_sectors; + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return ped_geometry_read (fs->geom, buf, sector, sector_count); +} + +int +fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_read_fragments (fs, buf, frag, 1); +} + +int +fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_frag_to_sector (fs, frag); + PedSector sector_count = count * fs_info->frag_sectors; + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return ped_geometry_write (fs->geom, buf, sector, sector_count); +} + +int +fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_write_fragments (fs, buf, frag, 1); +} + +int +fat_write_sync_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + if (!fat_write_fragments (fs, buf, frag, count)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +int +fat_write_sync_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_write_sync_fragments (fs, buf, frag, 1); +} + +int +fat_read_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_cluster_to_sector (fs, cluster); + PedSector sector_count = count * fs_info->cluster_sectors; + + PED_ASSERT (cluster >= 2 + && cluster + count - 1 < fs_info->cluster_count + 2); + + return ped_geometry_read (fs->geom, buf, sector, sector_count); +} + +int +fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + return fat_read_clusters (fs, buf, cluster, 1); +} + +int +fat_write_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_cluster_to_sector (fs, cluster); + PedSector sector_count = count * fs_info->cluster_sectors; + + PED_ASSERT (cluster >= 2 + && cluster + count - 1 < fs_info->cluster_count + 2); + + return ped_geometry_write (fs->geom, buf, sector, sector_count); +} + +int +fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + return fat_write_clusters (fs, buf, cluster, 1); +} + +int +fat_write_sync_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + if (!fat_write_clusters (fs, buf, cluster, count)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +int +fat_write_sync_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + if (!fat_write_cluster (fs, buf, cluster)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/fatio.h b/libparted/fs/fat/fatio.h new file mode 100644 index 0000000..ad236fa --- /dev/null +++ b/libparted/fs/fat/fatio.h @@ -0,0 +1,48 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef FATIO_H_INCLUDED +#define FATIO_H_INCLUDED + +#include "fat.h" + +extern int fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count); +extern int fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count); +extern int fat_write_sync_fragments (PedFileSystem* fs, char* buf, + FatFragment frag, FatFragment count); + +extern int fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag); +extern int fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag); +extern int fat_write_sync_fragment (PedFileSystem* fs, char* buf, + FatFragment frag); + +extern int fat_read_clusters (PedFileSystem* fs, char* buf, FatCluster cluster, + FatCluster count); +extern int fat_write_clusters (PedFileSystem* fs, char* buf, FatCluster cluster, + FatCluster count); +extern int fat_write_sync_clusters (PedFileSystem* fs, char* buf, + FatCluster cluster, FatCluster count); + +extern int fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster); +extern int fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster); +extern int fat_write_sync_cluster (PedFileSystem* fs, char *buf, + FatCluster cluster); + +#endif /* FATIO_H_INCLUDED */ diff --git a/libparted/fs/fat/resize.c b/libparted/fs/fat/resize.c new file mode 100644 index 0000000..2c0097a --- /dev/null +++ b/libparted/fs/fat/resize.c @@ -0,0 +1,877 @@ +/* + libparted + Copyright (C) 1998-2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "fat.h" +#include "traverse.h" +#include "count.h" +#include "fatio.h" +#include "calc.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <stdarg.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +/* Recursively builds (i.e. makes consistent) the duplicated directory tree + * (leaving the original directory tree in tact) + */ +static int +fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info) +{ + FatTraverseInfo* sub_dir_info; + FatDirEntry* dir_entry; + FatCluster old_first_cluster; + + while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) { + if (fat_dir_entry_is_null_term (dir_entry)) + break; + if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs)) + continue; + + fat_traverse_mark_dirty (trav_info); + + old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry, + ctx->old_fs); + fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs, + fat_op_context_map_cluster (ctx, old_first_cluster)); + + if (fat_dir_entry_is_directory (dir_entry) + && dir_entry->name [0] != '.') { + sub_dir_info + = fat_traverse_directory (trav_info, dir_entry); + if (!sub_dir_info) + return 0; + if (!fat_construct_directory (ctx, sub_dir_info)) + return 0; + } + } + /* remove "stale" entries at the end */ + while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) { + memset (dir_entry, 0, sizeof (FatDirEntry)); + fat_traverse_mark_dirty (trav_info); + } + fat_traverse_complete (trav_info); + return 1; +} + +static int +duplicate_legacy_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + PED_ASSERT (old_fs_info->root_dir_sector_count + == new_fs_info->root_dir_sector_count); + + if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer, + old_fs_info->root_dir_offset, + old_fs_info->root_dir_sector_count)) + return 0; + + if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer, + new_fs_info->root_dir_offset, + new_fs_info->root_dir_sector_count)) + return 0; + + return 1; +} + +/* + Constructs the new directory tree for legacy (FAT16) file systems. +*/ +static int +fat_construct_legacy_root (FatOpContext* ctx) +{ + FatTraverseInfo* trav_info; + + if (!duplicate_legacy_root_dir (ctx)) + return 0; + trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\"); + return fat_construct_directory (ctx, trav_info); +} + +/* + Constructs the new directory tree for new (FAT32) file systems. +*/ +static int +fat_construct_root (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatTraverseInfo* trav_info; + + trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster, + "\\"); + fat_construct_directory (ctx, trav_info); + return 1; +} + +/* Converts the root directory between FAT16 and FAT32. NOTE: this code + * can also do no conversion. I'm leaving fat_construct_directory(), because + * it's really pretty :-) It also leaves a higher chance of deleted file + * recovery, because it doesn't remove redundant entries. (We do this here, + * because brain-damaged FAT16 has an arbitary limit on root directory entries, + * so we save room) + */ +static int +fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav, + FatTraverseInfo* new_trav) +{ + FatTraverseInfo* sub_old_dir_trav; + FatTraverseInfo* sub_new_dir_trav; + FatDirEntry* new_dir_entry; + FatDirEntry* old_dir_entry; + FatCluster old_first_cluster; + + while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) { + if (fat_dir_entry_is_null_term (old_dir_entry)) + break; + if (!fat_dir_entry_is_active (old_dir_entry)) + continue; + + new_dir_entry = fat_traverse_next_dir_entry (new_trav); + if (!new_dir_entry) { + return ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("There's not enough room in the root " + "directory for all of the files. Either " + "cancel, or ignore to lose the files.")) + == PED_EXCEPTION_IGNORE; + } + + *new_dir_entry = *old_dir_entry; + fat_traverse_mark_dirty (new_trav); + + if (!fat_dir_entry_has_first_cluster (old_dir_entry, + ctx->old_fs)) + continue; + + old_first_cluster = fat_dir_entry_get_first_cluster ( + old_dir_entry, ctx->old_fs); + fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs, + fat_op_context_map_cluster (ctx, old_first_cluster)); + + if (fat_dir_entry_is_directory (old_dir_entry) + && old_dir_entry->name [0] != '.') { + sub_old_dir_trav + = fat_traverse_directory (old_trav, old_dir_entry); + sub_new_dir_trav + = fat_traverse_directory (new_trav, new_dir_entry); + if (!sub_old_dir_trav || !sub_new_dir_trav) + return 0; + + if (!fat_convert_directory (ctx, sub_old_dir_trav, + sub_new_dir_trav)) + return 0; + } + } + + /* remove "stale" entries at the end, just in case there is some + * overlap + */ + while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) { + memset (new_dir_entry, 0, sizeof (FatDirEntry)); + fat_traverse_mark_dirty (new_trav); + } + + fat_traverse_complete (old_trav); + fat_traverse_complete (new_trav); + return 1; +} + +static void +clear_cluster (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + memset (fs_info->buffer, 0, fs_info->cluster_size); + fat_write_cluster (fs, fs_info->buffer, cluster); +} + +/* This MUST be called BEFORE the fat_construct_new_fat(), because cluster + * allocation depend on the old FAT. The reason is, old clusters may + * still be needed during the resize, (particularly clusters in the directory + * tree) even if they will be discarded later. + */ +static int +alloc_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster i; + FatCluster cluster; + FatCluster cluster_count; + + PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32); + + cluster_count = ped_div_round_up ( + PED_MAX (16, old_fs_info->root_dir_sector_count), + new_fs_info->cluster_sectors); + + for (i = 0; i < cluster_count; i++) { + cluster = fat_table_alloc_check_cluster (new_fs_info->fat, + ctx->new_fs); + if (!cluster) + return 0; + ctx->new_root_dir [i] = cluster; + clear_cluster (ctx->new_fs, cluster); + } + ctx->new_root_dir [i] = 0; + new_fs_info->root_cluster = ctx->new_root_dir [0]; + return 1; +} + +/* when converting FAT32 -> FAT16 + * fat_duplicate clusters() duplicated the root directory unnecessarily. + * Let's free it. + * + * This must be called AFTER fat_construct_new_fat(). (otherwise, our + * changes just get overwritten) + */ +static int +free_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster old_cluster; + FatFragment i; + + PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32); + PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16); + + for (old_cluster = old_fs_info->root_cluster; + !fat_table_is_eof (old_fs_info->fat, old_cluster); + old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) { + FatFragment old_frag; + old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster); + for (i = 0; i < new_fs_info->cluster_frags; i++) { + FatFragment new_frag; + FatCluster new_clst; + new_frag = fat_op_context_map_fragment (ctx, + old_frag + i); + new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag); + if (!fat_table_set_avail (new_fs_info->fat, new_clst)) + return 0; + } + } + + return 1; +} + +static int +fat_clear_root_dir (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + + PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16); + PED_ASSERT (fs_info->root_dir_sector_count); + + memset (fs_info->buffer, 0, 512); + + for (i = 0; i < fs_info->root_dir_sector_count; i++) { + if (!ped_geometry_write (fs->geom, fs_info->buffer, + fs_info->root_dir_offset + i, 1)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Error writing to the root directory.")) + == PED_EXCEPTION_CANCEL) + return 0; + } + } + return 1; +} + +static int +fat_construct_converted_tree (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatTraverseInfo* old_trav_info; + FatTraverseInfo* new_trav_info; + + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_trav_info = fat_traverse_begin (ctx->new_fs, + new_fs_info->root_cluster, "\\"); + old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT, + "\\"); + } else { + fat_clear_root_dir (ctx->new_fs); + new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, + "\\"); + old_trav_info = fat_traverse_begin (ctx->old_fs, + old_fs_info->root_cluster, "\\"); + } + if (!new_trav_info || !old_trav_info) + return 0; + if (!fat_convert_directory (ctx, old_trav_info, new_trav_info)) + return 0; + return 1; +} + +/* + Constructs the new directory tree to match the new file locations. +*/ +static int +fat_construct_dir_tree (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + + if (new_fs_info->fat_type == old_fs_info->fat_type) { + switch (old_fs_info->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + return fat_construct_legacy_root (ctx); + + case FAT_TYPE_FAT32: + return fat_construct_root (ctx); + } + } else { + return fat_construct_converted_tree (ctx); + } + + return 0; +} + +static FatFragment +_get_next_old_frag (FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatCluster cluster; + FatCluster next_cluster; + + if ((frag + 1) % old_fs_info->cluster_frags != 0) { + if (fat_is_fragment_active (ctx->old_fs, frag + 1)) + return frag + 1; + else + return -1; + } else { + cluster = fat_frag_to_cluster (ctx->old_fs, frag); + next_cluster = fat_table_get (old_fs_info->fat, cluster); + + if (fat_table_is_eof (old_fs_info->fat, next_cluster)) + return -1; + else + return fat_cluster_to_frag (ctx->old_fs, next_cluster); + } +} + +/* + Constructs the new fat for the resized file system. +*/ +static int +fat_construct_new_fat (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment old_frag; + FatCluster new_cluster; + FatFragment new_frag; + FatFragment old_next_frag; + FatFragment new_next_frag; + FatCluster new_next_cluster; + FatClusterFlag flag; + int i; + + fat_table_clear (new_fs_info->fat); + if (!fat_table_set_cluster_count (new_fs_info->fat, + new_fs_info->cluster_count)) + return 0; + + for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) { + flag = fat_get_fragment_flag (ctx->old_fs, old_frag); + if (flag == FAT_FLAG_FREE) + continue; + if (flag == FAT_FLAG_BAD) { + new_frag = fat_op_context_map_static_fragment ( + ctx, old_frag); + if (new_frag == -1) + continue; + new_cluster = fat_frag_to_cluster (ctx->new_fs, + new_frag); + fat_table_set_bad (new_fs_info->fat, new_cluster); + continue; + } + + new_frag = fat_op_context_map_fragment (ctx, old_frag); + new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag); + + old_next_frag = _get_next_old_frag (ctx, old_frag); + if (old_next_frag == -1) { + fat_table_set_eof (new_fs_info->fat, new_cluster); + continue; + } + + new_next_frag = fat_op_context_map_fragment (ctx, + old_next_frag); + PED_ASSERT (new_next_frag != -1); + + new_next_cluster = fat_frag_to_cluster (ctx->new_fs, + new_next_frag); + PED_ASSERT (new_next_cluster != new_cluster); + + fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster); + } + +#if 0 +#ifdef PED_VERBOSE + for (old_cluster=2; old_cluster < old_fs_info->cluster_count+2; + old_cluster++) { + if (fat_table_is_available (old_fs_info->fat, old_cluster)) + continue; + + printf ("%d->%d\t(next: %d->%d)\n", + old_cluster, + ctx->remap [old_cluster], + fat_table_get (old_fs_info->fat, old_cluster), + fat_table_get (new_fs_info->fat, + ctx->remap [old_cluster])); + } +#endif /* PED_VERBOSE */ +#endif + + if (old_fs_info->fat_type == FAT_TYPE_FAT32 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_fs_info->root_cluster + = fat_op_context_map_cluster (ctx, + old_fs_info->root_cluster); + } + + if (old_fs_info->fat_type == FAT_TYPE_FAT16 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + for (i=0; ctx->new_root_dir[i+1]; i++) { + fat_table_set (new_fs_info->fat, + ctx->new_root_dir[i], + ctx->new_root_dir[i+1]); + } + fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]); + } + + return 1; +} + +static int +ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedExceptionOption status; + const char* fat16_msg; + const char* fat32_msg; + + if (fs_info->fat_type == FAT_TYPE_FAT16) + fat16_msg = _("If you leave your file system as FAT16, " + "then you will have no problems."); + else + fat16_msg = _("If you convert to FAT16, and MS Windows " + "is installed on this partition, then " + "you must re-install the MS Windows boot " + "loader. If you want to do this, you " + "should consult the Parted manual (or " + "your distribution's manual)."); + + if (fs_info->fat_type == FAT_TYPE_FAT32) + fat32_msg = _("If you leave your file system as FAT32, " + "then you will not introduce any new " + "problems."); + else + fat32_msg = _("If you convert to FAT32, and MS Windows " + "is installed on this partition, then " + "you must re-install the MS Windows boot " + "loader. If you want to do this, you " + "should consult the Parted manual (or " + "your distribution's manual). Also, " + "converting to FAT32 will make the file " + "system unreadable by MS DOS, MS Windows " + "95a, and MS Windows NT."); + + if (fat16_ok && fat32_ok) { + status = ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_YES_NO_CANCEL, + _("%s %s %s"), + _("Would you like to use FAT32?"), + fat16_msg, + fat32_msg); + + switch (status) { + case PED_EXCEPTION_YES: + *out_fat_type = FAT_TYPE_FAT32; + return 1; + + case PED_EXCEPTION_NO: + *out_fat_type = FAT_TYPE_FAT16; + return 1; + + case PED_EXCEPTION_UNHANDLED: + *out_fat_type = fs_info->fat_type; + return 1; + + case PED_EXCEPTION_CANCEL: + return 0; + + default: + PED_ASSERT (0); + break; + } + } + + if (fat16_ok) { + if (fs_info->fat_type != FAT_TYPE_FAT16) { + status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("%s %s"), + _("The file system can only be resized to this " + "size by converting to FAT16."), + fat16_msg); + if (status == PED_EXCEPTION_CANCEL) + return 0; + } + *out_fat_type = FAT_TYPE_FAT16; + return 1; + } + + if (fat32_ok) { + if (fs_info->fat_type != FAT_TYPE_FAT32) { + status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("%s %s"), + _("The file system can only be resized to this " + "size by converting to FAT32."), + fat32_msg); + if (status == PED_EXCEPTION_CANCEL) + return 0; + } + *out_fat_type = FAT_TYPE_FAT32; + return 1; + } + + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("GNU Parted cannot resize this partition to this size. " + "We're working on it!")); + + return 0; +} + +/* For resize operations: determine if the file system must be FAT16 or FAT32, + * or either. If the new file system must be FAT32, then query for + * confirmation. If either file system can be used, query for which one. + */ +static int +get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom, + FatType* out_fat_type) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector fat16_cluster_sectors; + PedSector fat32_cluster_sectors; + FatCluster dummy_cluster_count; + PedSector dummy_fat_sectors; + int fat16_ok; + int fat32_ok; + + fat16_ok = fat_calc_resize_sizes ( + new_geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT16, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &fat16_cluster_sectors, + &dummy_cluster_count, + &dummy_fat_sectors); + + fat32_ok = fat_calc_resize_sizes ( + new_geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT32, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &fat32_cluster_sectors, + &dummy_cluster_count, + &dummy_fat_sectors); + + return ask_type (fs, fat16_ok, fat32_ok, out_fat_type); +} + +/* Creates the PedFileSystem struct for the new resized file system, and + sticks it in a FatOpContext. At the end of the process, the original + (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs). + */ +static FatOpContext* +create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatSpecific* new_fs_info; + PedFileSystem* new_fs; + PedSector new_cluster_sectors; + FatCluster new_cluster_count; + PedSector new_fat_sectors; + FatType new_fat_type; + PedSector root_dir_sector_count; + FatOpContext* context; + + /* hypothetical number of root dir sectors, if we end up using + * FAT16 + */ + if (fs_info->root_dir_sector_count) + root_dir_sector_count = fs_info->root_dir_sector_count; + else + root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT + * sizeof (FatDirEntry) / 512; + + if (!get_fat_type (fs, new_geom, &new_fat_type)) + return 0; + + fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type, + root_dir_sector_count, fs_info->cluster_sectors, + &new_cluster_sectors, &new_cluster_count, &new_fat_sectors); + + if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors, + new_cluster_count)) + goto error; + + new_fs = fat_alloc (new_geom); + if (!new_fs) + goto error; + + new_fs_info = FAT_SPECIFIC (new_fs); + if (!new_fs_info) + goto error_free_new_fs; + +/* preserve boot code, etc. */ + memcpy (&new_fs_info->boot_sector, &fs_info->boot_sector, + sizeof (FatBootSector)); + memcpy (&new_fs_info->info_sector, &fs_info->info_sector, + sizeof (FatInfoSector)); + + new_fs_info->logical_sector_size = fs_info->logical_sector_size; + new_fs_info->sector_count = new_geom->length; + + new_fs_info->sectors_per_track = fs_info->sectors_per_track; + new_fs_info->heads = fs_info->heads; + + new_fs_info->cluster_size = new_cluster_sectors * 512; + new_fs_info->cluster_sectors = new_cluster_sectors; + new_fs_info->cluster_count = new_cluster_count; + new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster; + + new_fs_info->fat_type = new_fat_type; + new_fs_info->fat_table_count = 2; + new_fs_info->fat_sectors = new_fat_sectors; + + /* what about copying? */ + new_fs_info->serial_number = fs_info->serial_number; + + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_fs_info->info_sector_offset = 1; + new_fs_info->boot_sector_backup_offset = 6; + + new_fs_info->root_dir_offset = 0; + new_fs_info->root_dir_entry_count = 0; + new_fs_info->root_dir_sector_count = 0; + + /* we add calc_align_sectors to push the cluster_offset + forward, to keep the clusters aligned between the new + and old file systems + */ + new_fs_info->fat_offset + = fat_min_reserved_sector_count (FAT_TYPE_FAT32) + + fat_calc_align_sectors (new_fs, fs); + + new_fs_info->cluster_offset + = new_fs_info->fat_offset + + 2 * new_fs_info->fat_sectors; + } else { + new_fs_info->root_dir_sector_count = root_dir_sector_count; + new_fs_info->root_dir_entry_count + = root_dir_sector_count * 512 / sizeof (FatDirEntry); + + new_fs_info->fat_offset + = fat_min_reserved_sector_count (FAT_TYPE_FAT16) + + fat_calc_align_sectors (new_fs, fs); + + new_fs_info->root_dir_offset = new_fs_info->fat_offset + + 2 * new_fs_info->fat_sectors; + + new_fs_info->cluster_offset = new_fs_info->root_dir_offset + + new_fs_info->root_dir_sector_count; + } + + new_fs_info->total_dir_clusters = fs_info->total_dir_clusters; + + context = fat_op_context_new (new_fs, fs); + if (!context) + goto error_free_new_fs_info; + + if (!fat_op_context_create_initial_fat (context)) + goto error_free_context; + + if (!fat_alloc_buffers (new_fs)) + goto error_free_fat; + + return context; + +error_free_fat: + fat_table_destroy (new_fs_info->fat); +error_free_context: + free (context); +error_free_new_fs_info: + free (new_fs_info); +error_free_new_fs: + free (new_fs); +error: + return NULL; +} + +static int +resize_context_assimilate (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + fat_free_buffers (ctx->old_fs); + fat_table_destroy (old_fs_info->fat); + free (old_fs_info); + ped_geometry_destroy (ctx->old_fs->geom); + + ctx->old_fs->type_specific = ctx->new_fs->type_specific; + ctx->old_fs->geom = ctx->new_fs->geom; + ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16) + ? &fat16_type + : &fat32_type; + + free (ctx->new_fs); + + fat_op_context_destroy (ctx); + + return 1; +} + +static int +resize_context_abort (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + fat_free_buffers (ctx->new_fs); + fat_table_destroy (new_fs_info->fat); + free (new_fs_info); + ped_geometry_destroy (ctx->new_fs->geom); + free (ctx->new_fs); + + fat_op_context_destroy (ctx); + + return 1; +} + +/* copies the "hidden" sectors, between the boot sector and the FAT. Required, + * for the Windows 98 FAT32 boot loader + */ +int +_copy_hidden_sectors (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + PedSector first = 1; + PedSector last; + PedSector count; + + /* nothing to copy for FAT16 */ + if (old_fs_info->fat_type == FAT_TYPE_FAT16 + || new_fs_info->fat_type == FAT_TYPE_FAT16) + return 1; + + last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1; + count = last - first + 1; + + PED_ASSERT (count < BUFFER_SIZE); + + if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer, + first, count)) + return 0; + if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer, + first, count)) + return 0; + return 1; +} + +int +fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatSpecific* new_fs_info; + FatOpContext* ctx; + PedFileSystem* new_fs; + + ctx = create_resize_context (fs, geom); + if (!ctx) + goto error; + new_fs = ctx->new_fs; + new_fs_info = FAT_SPECIFIC (new_fs); + + if (!fat_duplicate_clusters (ctx, timer)) + goto error_abort_ctx; + if (fs_info->fat_type == FAT_TYPE_FAT16 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + if (!alloc_root_dir (ctx)) + goto error_abort_ctx; + } + if (!fat_construct_new_fat (ctx)) + goto error_abort_ctx; + if (fs_info->fat_type == FAT_TYPE_FAT32 + && new_fs_info->fat_type == FAT_TYPE_FAT16) { + if (!free_root_dir (ctx)) + goto error_abort_ctx; + } + if (!fat_construct_dir_tree (ctx)) + goto error_abort_ctx; + if (!fat_table_write_all (new_fs_info->fat, new_fs)) + goto error_abort_ctx; + + _copy_hidden_sectors (ctx); + fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs); + fat_boot_sector_write (&new_fs_info->boot_sector, new_fs); + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + fat_info_sector_generate (&new_fs_info->info_sector, new_fs); + fat_info_sector_write (&new_fs_info->info_sector, new_fs); + } + + if (!resize_context_assimilate (ctx)) + goto error; + + return 1; + +error_abort_ctx: + resize_context_abort (ctx); +error: + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/table.c b/libparted/fs/fat/table.c new file mode 100644 index 0000000..b77e372 --- /dev/null +++ b/libparted/fs/fat/table.c @@ -0,0 +1,480 @@ +/* + libparted + Copyright (C) 1998-2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include <parted/endian.h> +#include "fat.h" + +#ifndef DISCOVER_ONLY + +FatTable* +fat_table_new (FatType fat_type, FatCluster size) +{ + FatTable* ft; + int entry_size = fat_table_entry_size (fat_type); + + ft = (FatTable*) ped_malloc (sizeof (FatTable)); + if (!ft) return NULL; + + ft->cluster_count = ft->free_cluster_count = size - 2; + +/* ensure there's some free room on the end, to finish off the sector */ + ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size; + ft->fat_type = fat_type; + ft->raw_size = ft->size * entry_size; + + ft->table = ped_malloc (ft->raw_size); + if (!ft->table) { + free (ft); + return NULL; + } + + fat_table_clear (ft); + return ft; +} + +void +fat_table_destroy (FatTable* ft) +{ + free (ft->table); + free (ft); +} + +FatTable* +fat_table_duplicate (const FatTable* ft) +{ + FatTable* dup_ft; + + dup_ft = fat_table_new (ft->fat_type, ft->size); + if (!dup_ft) return NULL; + + dup_ft->cluster_count = ft->cluster_count; + dup_ft->free_cluster_count = ft->free_cluster_count; + dup_ft->bad_cluster_count = ft->bad_cluster_count; + dup_ft->last_alloc = ft->last_alloc; + + memcpy (dup_ft->table, ft->table, ft->raw_size); + + return dup_ft; +} + +void +fat_table_clear (FatTable* ft) +{ + memset (ft->table, 0, ft->raw_size); + + fat_table_set (ft, 0, 0x0ffffff8); + fat_table_set (ft, 1, 0x0fffffff); + + ft->free_cluster_count = ft->cluster_count; + ft->bad_cluster_count = 0; + ft->last_alloc = 1; +} + +int +fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count) +{ + PED_ASSERT (new_cluster_count + 2 <= ft->size); + + ft->cluster_count = new_cluster_count; + return fat_table_count_stats (ft); +} + +int +fat_table_count_stats (FatTable* ft) +{ + FatCluster i; + + PED_ASSERT (ft->cluster_count + 2 <= ft->size); + + ft->free_cluster_count = 0; + ft->bad_cluster_count = 0; + + for (i=2; i < ft->cluster_count + 2; i++) { + if (fat_table_is_available (ft, i)) + ft->free_cluster_count++; + if (fat_table_is_bad (ft, i)) + ft->bad_cluster_count++; + } + return 1; +} + +int +fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512); + + memset (ft->table, 0, ft->raw_size); + + if (!ped_geometry_read (fs->geom, (void *) ft->table, + fs_info->fat_offset + + table_num * fs_info->fat_sectors, + fs_info->fat_sectors)) + return 0; + + if ( *((unsigned char*) ft->table) != fs_info->boot_sector.media) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("FAT %d media %x doesn't match the boot sector's " + "media %x. You should probably run scandisk."), + (int) table_num + 1, + (int) *((unsigned char*) ft->table), + (int) fs_info->boot_sector.media) + != PED_EXCEPTION_IGNORE) + return 0; + } + + ft->cluster_count = fs_info->cluster_count; + + fat_table_count_stats (ft); + + return 1; +} + +int +fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512); + + if (!ped_geometry_write (fs->geom, (void *) ft->table, + fs_info->fat_offset + + table_num * fs_info->fat_sectors, + fs_info->fat_sectors)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + + return 1; +} + +int +fat_table_write_all (const FatTable* ft, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + + for (i = 0; i < fs_info->fat_table_count; i++) { + if (!fat_table_write (ft, fs, i)) + return 0; + } + + return 1; +} + +int +fat_table_compare (const FatTable* a, const FatTable* b) +{ + FatCluster i; + + if (a->cluster_count != b->cluster_count) + return 0; + + for (i = 0; i < a->cluster_count + 2; i++) { + if (fat_table_get (a, i) != fat_table_get (b, i)) + return 0; + } + + return 1; +} + +static int +_test_code_available (const FatTable* ft, FatCluster code) +{ + return code == 0; +} + +static int +_test_code_bad (const FatTable* ft, FatCluster code) +{ + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + if (code == 0xff7) return 1; + break; + + case FAT_TYPE_FAT16: + if (code == 0xfff7) return 1; + break; + + case FAT_TYPE_FAT32: + if (code == 0x0ffffff7) return 1; + break; + } + return 0; +} + +static int +_test_code_eof (const FatTable* ft, FatCluster code) +{ + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + if (code >= 0xff7) return 1; + break; + + case FAT_TYPE_FAT16: + if (code >= 0xfff7) return 1; + break; + + case FAT_TYPE_FAT32: + if (code >= 0x0ffffff7) return 1; + break; + } + return 0; +} + +void +_update_stats (FatTable* ft, FatCluster cluster, FatCluster value) +{ + if (_test_code_available (ft, value) + && !fat_table_is_available (ft, cluster)) { + ft->free_cluster_count++; + if (fat_table_is_bad (ft, cluster)) + ft->bad_cluster_count--; + } + + if (!_test_code_available (ft, value) + && fat_table_is_available (ft, cluster)) { + ft->free_cluster_count--; + if (_test_code_bad (ft, cluster)) + ft->bad_cluster_count--; + } +} + +int +fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value) +{ + if (cluster >= ft->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("fat_table_set: cluster %ld outside " + "file system"), + (long) cluster); + return 0; + } + + _update_stats (ft, cluster, value); + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + ((unsigned short *) ft->table) [cluster] + = PED_CPU_TO_LE16 (value); + break; + + case FAT_TYPE_FAT32: + ((unsigned int *) ft->table) [cluster] + = PED_CPU_TO_LE32 (value); + break; + } + return 1; +} + +FatCluster +fat_table_get (const FatTable* ft, FatCluster cluster) +{ + if (cluster >= ft->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("fat_table_get: cluster %ld outside " + "file system"), + (long) cluster); + exit (EXIT_FAILURE); /* FIXME */ + } + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + return PED_LE16_TO_CPU + (((unsigned short *) ft->table) [cluster]); + + case FAT_TYPE_FAT32: + return PED_LE32_TO_CPU + (((unsigned int *) ft->table) [cluster]); + } + + return 0; +} + +FatCluster +fat_table_alloc_cluster (FatTable* ft) +{ + FatCluster i; + FatCluster cluster; + +/* hack: assumes the first two FAT entries are marked as used (which they + * always should be) + */ + for (i=1; i < ft->cluster_count + 1; i++) { + cluster = (i + ft->last_alloc) % ft->cluster_count; + if (fat_table_is_available (ft, cluster)) { + ft->last_alloc = cluster; + return cluster; + } + } + + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("fat_table_alloc_cluster: no free clusters")); + return 0; +} + +FatCluster +fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster result; + + while (1) { + result = fat_table_alloc_cluster (ft); + if (!result) + return 0; + if (fat_read_cluster (fs, fs_info->buffer, result)) + return result; + fat_table_set_bad (ft, result); + } +} + +/* + returns true if <cluster> is marked as bad +*/ +int +fat_table_is_bad (const FatTable* ft, FatCluster cluster) +{ + return _test_code_bad (ft, fat_table_get (ft, cluster)); +} + +/* + returns true if <cluster> represents an EOF marker +*/ +int +fat_table_is_eof (const FatTable* ft, FatCluster cluster) +{ + return _test_code_eof (ft, cluster); +} + +/* + returns true if <cluster> is available. +*/ +int +fat_table_is_available (const FatTable* ft, FatCluster cluster) +{ + return _test_code_available (ft, fat_table_get (ft, cluster)); +} + +/* + returns true if <cluster> is empty. Note that this includes bad clusters. +*/ +int +fat_table_is_empty (const FatTable* ft, FatCluster cluster) +{ + return fat_table_is_available (ft, cluster) + || fat_table_is_bad (ft, cluster); +} + +/* + returns true if <cluster> is being used for something constructive. +*/ +int +fat_table_is_active (const FatTable* ft, FatCluster cluster) +{ + return !fat_table_is_bad (ft, cluster) + && !fat_table_is_available (ft, cluster); +} + +/* + marks <cluster> as the last cluster in the chain +*/ +int +fat_table_set_eof (FatTable* ft, FatCluster cluster) +{ + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + return fat_table_set (ft, cluster, 0xfff8); + + case FAT_TYPE_FAT32: + return fat_table_set (ft, cluster, 0x0fffffff); + } + + return 0; +} + +/* + Marks a clusters as unusable, due to physical disk damage. +*/ +int +fat_table_set_bad (FatTable* ft, FatCluster cluster) +{ + if (!fat_table_is_bad (ft, cluster)) + ft->bad_cluster_count++; + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + return fat_table_set (ft, cluster, 0xff7); + + case FAT_TYPE_FAT16: + return fat_table_set (ft, cluster, 0xfff7); + + case FAT_TYPE_FAT32: + return fat_table_set (ft, cluster, 0x0ffffff7); + } + + return 0; +} + +/* + marks <cluster> as unused/free/available +*/ +int +fat_table_set_avail (FatTable* ft, FatCluster cluster) +{ + return fat_table_set (ft, cluster, 0); +} + +#endif /* !DISCOVER_ONLY */ + +int +fat_table_entry_size (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 2; /* FIXME: how? */ + + case FAT_TYPE_FAT16: + return 2; + + case FAT_TYPE_FAT32: + return 4; + } + + return 0; +} diff --git a/libparted/fs/fat/table.h b/libparted/fs/fat/table.h new file mode 100644 index 0000000..e25d82f --- /dev/null +++ b/libparted/fs/fat/table.h @@ -0,0 +1,73 @@ +/* + libparted + Copyright (C) 1998-2000, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_FAT_TABLE_H_INCLUDED +#define PED_FAT_TABLE_H_INCLUDED + +typedef struct _FatTable FatTable; + +#include "fat.h" + +struct _FatTable { + void* table; + FatCluster size; + int raw_size; + + FatType fat_type; + FatCluster cluster_count; + FatCluster free_cluster_count; + FatCluster bad_cluster_count; + + FatCluster last_alloc; +}; + +extern FatTable* fat_table_new (FatType fat_type, FatCluster size); +extern FatTable* fat_table_duplicate (const FatTable* ft); +extern void fat_table_destroy (FatTable* ft); +extern void fat_table_clear (FatTable* ft); +extern int fat_table_set_cluster_count (FatTable* ft, + FatCluster new_cluster_count); + +extern int fat_table_read (FatTable* ft, const PedFileSystem* fs, + int table_num); +extern int fat_table_write (const FatTable* ft, PedFileSystem* fs, + int table_num); +extern int fat_table_write_all (const FatTable* ft, PedFileSystem* fs); +extern int fat_table_compare (const FatTable* a, const FatTable* b); +extern int fat_table_count_stats (FatTable* ft); + +extern FatCluster fat_table_get (const FatTable* ft, FatCluster cluster); +extern int fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value); + +extern FatCluster fat_table_alloc_cluster (FatTable* ft); +extern FatCluster fat_table_alloc_check_cluster (FatTable* ft, + PedFileSystem* fs); + +extern int fat_table_is_bad (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_eof (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_empty (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_available (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_active (const FatTable* ft, FatCluster cluster); + +extern int fat_table_set_eof (FatTable* ft, FatCluster cluster); +extern int fat_table_set_avail (FatTable* ft, FatCluster cluster); +extern int fat_table_set_bad (FatTable* ft, FatCluster cluster); + +extern int fat_table_entry_size (FatType fat_type); + +#endif /* PED_FAT_TABLE_H_INCLUDED */ diff --git a/libparted/fs/fat/traverse.c b/libparted/fs/fat/traverse.c new file mode 100644 index 0000000..7cecfe3 --- /dev/null +++ b/libparted/fs/fat/traverse.c @@ -0,0 +1,367 @@ +/* + libparted + Copyright (C) 1998-2000, 2005, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "fat.h" +#include "traverse.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +#define NO_CLUSTER -1 + +static char tmp_buffer [4096]; + +int +fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info) +{ + return trav_info->buffer_size / sizeof (FatDirEntry); +} + +/* returns 1 if there are no more directory entries in the directory being + * traversed, 0 otherwise. + */ +static int +is_last_buffer (FatTraverseInfo* trav_info) { + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + if (trav_info->is_legacy_root_dir) + return 1; + else + return fat_table_is_eof (fs_info->fat, trav_info->next_buffer); +} + +static int +write_root_dir (FatTraverseInfo* trav_info) +{ + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count)) + return 0; + if (!ped_geometry_sync (trav_info->fs->geom)) + return 0; + trav_info->dirty = 0; + return 1; +} + +static int +write_dir_cluster (FatTraverseInfo* trav_info) +{ + if (!fat_write_sync_cluster (trav_info->fs, + (void*) trav_info->dir_entries, + trav_info->this_buffer)) + return 0; + trav_info->dirty = 0; + return 1; +} + +static int +write_dir_buffer (FatTraverseInfo* trav_info) +{ + if (trav_info->is_legacy_root_dir) + return write_root_dir (trav_info); + else + return write_dir_cluster (trav_info); +} + +static int +read_next_dir_buffer (FatTraverseInfo* trav_info) +{ + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + PED_ASSERT (!trav_info->is_legacy_root_dir); + + trav_info->this_buffer = trav_info->next_buffer; + + if (trav_info->this_buffer < 2 + || trav_info->this_buffer >= fs_info->cluster_count + 2) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "Cluster %ld in directory %s is outside file system!", + (long) trav_info->this_buffer, + trav_info->dir_name); + return 0; + } + + trav_info->next_buffer + = fat_table_get (fs_info->fat, trav_info->this_buffer); + + return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries, + trav_info->this_buffer); +} + +/* FIXME: put into fat_dir_entry_* operations */ +void +fat_traverse_mark_dirty (FatTraverseInfo* trav_info) +{ + trav_info->dirty = 1; +} + +FatTraverseInfo* +fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster, + const char* dir_name) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTraverseInfo* trav_info; + + trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo)); + if (!trav_info) + goto error; + + trav_info->dir_name = strdup (dir_name); + if (!trav_info->dir_name) + goto error_free_trav_info; + + trav_info->fs = fs; + trav_info->is_legacy_root_dir + = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0); + trav_info->dirty = 0; + trav_info->eof = 0; + trav_info->current_entry = -1; + + if (trav_info->is_legacy_root_dir) { + trav_info->buffer_size = 512 * fs_info->root_dir_sector_count; + } else { + trav_info->next_buffer = start_cluster; + trav_info->buffer_size = fs_info->cluster_size; + } + + trav_info->dir_entries + = (FatDirEntry*) ped_malloc (trav_info->buffer_size); + if (!trav_info->dir_entries) + goto error_free_dir_name; + + if (trav_info->is_legacy_root_dir) { + if (!ped_geometry_read (fs->geom, trav_info->dir_entries, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count)) + goto error_free_dir_entries; + } else { + if (!read_next_dir_buffer (trav_info)) + goto error_free_dir_entries; + } + + return trav_info; + +error_free_dir_entries: + free (trav_info->dir_entries); +error_free_dir_name: + free (trav_info->dir_name); +error_free_trav_info: + free (trav_info); +error: + return NULL; +} + +int +fat_traverse_complete (FatTraverseInfo* trav_info) +{ + if (trav_info->dirty) { + if (!write_dir_buffer (trav_info)) + return 0; + } + free (trav_info->dir_entries); + free (trav_info->dir_name); + free (trav_info); + return 1; +} + +FatTraverseInfo* +fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent) +{ + strcpy (tmp_buffer, trav_info->dir_name); + fat_dir_entry_get_name (parent, + tmp_buffer + strlen (trav_info->dir_name)); + strcat (tmp_buffer, "\\"); + + return fat_traverse_begin (trav_info->fs, + fat_dir_entry_get_first_cluster (parent, trav_info->fs), + tmp_buffer); +} + +FatDirEntry* +fat_traverse_next_dir_entry (FatTraverseInfo *trav_info) +{ + if (trav_info->eof) + return NULL; + + trav_info->current_entry++; + if (trav_info->current_entry + >= fat_traverse_entries_per_buffer (trav_info)) { + if (trav_info->dirty) { + if (!write_dir_buffer (trav_info)) + return NULL; + } + + trav_info->current_entry = 0; + if (is_last_buffer (trav_info)) { + trav_info->eof = 1; + return NULL; + } + if (!read_next_dir_buffer (trav_info)) + return NULL; + } + return trav_info->dir_entries + trav_info->current_entry; +} + +FatCluster +fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + switch (fs_info->fat_type) { + case FAT_TYPE_FAT12: + case FAT_TYPE_FAT16: + return PED_LE16_TO_CPU (dir_entry->first_cluster); + + case FAT_TYPE_FAT32: + return PED_LE16_TO_CPU (dir_entry->first_cluster_high) + * 65536L + + PED_LE16_TO_CPU (dir_entry->first_cluster); + } + + return 0; +} + +void +fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs, + FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + switch (fs_info->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster); + break; + + case FAT_TYPE_FAT32: + dir_entry->first_cluster + = PED_CPU_TO_LE16 (cluster & 0xffff); + dir_entry->first_cluster_high + = PED_CPU_TO_LE16 (cluster / 0x10000); + break; + } +} + +uint32_t +fat_dir_entry_get_length (FatDirEntry* dir_entry) +{ + return PED_LE32_TO_CPU (dir_entry->length); +} + +int +fat_dir_entry_is_null_term (const FatDirEntry* dir_entry) +{ + FatDirEntry null_entry; + + memset (&null_entry, 0, sizeof (null_entry)); + return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0; +} + +int +fat_dir_entry_is_active (FatDirEntry* dir_entry) +{ + if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0; + if ((unsigned char) dir_entry->name[0] == 0) return 0; + if ((unsigned char) dir_entry->name[0] == 0xF6) return 0; + return 1; +} + +int +fat_dir_entry_is_file (FatDirEntry* dir_entry) { + if (dir_entry->attributes == VFAT_ATTR) return 0; + if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; + if (!fat_dir_entry_is_active (dir_entry)) return 0; + if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0; + return 1; +} + +int +fat_dir_entry_is_system_file (FatDirEntry* dir_entry) +{ + if (!fat_dir_entry_is_file (dir_entry)) return 0; + return (dir_entry->attributes & SYSTEM_ATTR) + || (dir_entry->attributes & HIDDEN_ATTR); +} + +int +fat_dir_entry_is_directory (FatDirEntry* dir_entry) +{ + if (dir_entry->attributes == VFAT_ATTR) return 0; + if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; + if (!fat_dir_entry_is_active (dir_entry)) return 0; + return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR; +} + +int +fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster first_cluster; + + if (!fat_dir_entry_is_file (dir_entry) + && !fat_dir_entry_is_directory (dir_entry)) + return 0; + + first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs); + if (first_cluster == 0 + || fat_table_is_eof (fs_info->fat, first_cluster)) + return 0; + + return 1; +} + +/* + decrypts silly DOS names to FILENAME.EXT +*/ +void +fat_dir_entry_get_name (const FatDirEntry *dir_entry, char *result) { + size_t i; + const char *src; + const char *ext; + + src = dir_entry->name; + + for (i=0; i < sizeof dir_entry->name; i++) { + if (src[i] == ' ' || src[i] == 0) break; + *result++ = src[i]; + } + + ext = (const char *) dir_entry->extension; + if (ext[0] != ' ' && ext[0] != 0) { + *result++ = '.'; + for (i=0; i < sizeof dir_entry->extension; i++) { + if (ext[i] == ' ' || ext[i] == 0) break; + *result++ = ext[i]; + } + } + + *result = 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/fat/traverse.h b/libparted/fs/fat/traverse.h new file mode 100644 index 0000000..1ad3c37 --- /dev/null +++ b/libparted/fs/fat/traverse.h @@ -0,0 +1,74 @@ +/* + libparted + Copyright (C) 1998-2000, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef TRAVERSE_H_INCLUDED +#define TRAVERSE_H_INCLUDED + +#include "fatio.h" + +typedef struct _FatTraverseInfo FatTraverseInfo; + +struct _FatTraverseInfo { + PedFileSystem* fs; + char* dir_name; + + int is_legacy_root_dir; + int dirty; + int eof; + + FatDirEntry* dir_entries; + int current_entry; + FatCluster this_buffer, next_buffer; + int buffer_size; +}; + +extern int fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info); + +/* starts traversal at an arbitary cluster. if start_cluster==0, then uses + root directory */ +extern FatTraverseInfo* fat_traverse_begin (PedFileSystem* fs, + FatCluster start_cluster, + const char* dir_name); + +extern int fat_traverse_complete (FatTraverseInfo* trav_info); + +extern FatTraverseInfo* fat_traverse_directory (FatTraverseInfo* trav_info, + FatDirEntry* parent); + +extern void fat_traverse_mark_dirty (FatTraverseInfo* trav_info); + +extern FatDirEntry* fat_traverse_next_dir_entry (FatTraverseInfo* trav_info); + +extern FatCluster fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs); + +extern void fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs, FatCluster cluster); + +extern uint32_t fat_dir_entry_get_length (FatDirEntry* dir_entry); + +extern int fat_dir_entry_is_null_term (const FatDirEntry* dir_entry); +extern int fat_dir_entry_is_file (FatDirEntry* dir_entry); +extern int fat_dir_entry_is_system_file (FatDirEntry* dir_entry); +extern int fat_dir_entry_is_directory (FatDirEntry* dir_entry); +extern void fat_dir_entry_get_name (const FatDirEntry* dir_entry, char* result); +extern int fat_dir_entry_is_active (FatDirEntry* dir_entry); +extern int fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs); + +#endif /* TRAVERSE_H_INCLUDED */ diff --git a/libparted/fs/hfs/DOC b/libparted/fs/hfs/DOC new file mode 100644 index 0000000..9ee8b4d --- /dev/null +++ b/libparted/fs/hfs/DOC @@ -0,0 +1,92 @@ +WARNING : Both HFS and HFS+ implementations of Linux 2.4 are buggy, at +least when used before or after this implementation. Some workarounds +are used in this implementation, but there can still be incompatibilities. +Try Linux 2.6 if you want to play with HFS(+) resizing (though some bugs +might also be there in 2.6, there is of course no warranty) + +--- + + Technical doc about Apple HFS and HFS+ file systems is available at : + * For HFS, section "Data Organization on Volumes", + "Chapter 2 - File Manager" + of the book "Inside Macintosh: Files" + http://developer.apple.com/documentation/mac/Files/Files-99.html + * For HFS+, "Technical Note TN1150", "HFS Plus Volume Format" + http://developer.apple.com/technotes/tn/tn1150.html + + Some useful HFS precisions concerning alignement, bit ordering, and + order of fields for extent key comparaisons are only in the HFS+ TN + + These Apple Creator Codes are reserved for us : + Shnk traP GP16 GnuP PH+x Xpnd Resz GP17 GP18 GP19 GP20 + +--- + +* Cache design * + +Versions before HFS Patch 15 were very slow when data relocation was needed, +because every extent to relocate involved scanning the whole file system, +looking for a reference to its physical position on the volume (this was a +dummy algorithm, I know :) + +HFS Patch 16 introduced a cache that allows to efficiently retrieve the place +of the reference in the file system given the physical position of an extent. +The cache is designed for : - efficiency + - scaling + - simplicity + - avoiding memory allocation while resizing + +This cache involves quite big worst case memory consumption, but without it +the time needed to complete the operation in the worst case would be huge +anyway (maybe several years...) so this isn't really an issue. The cache size +is nearly proportional to the number of files you have, or if you have very few +files, to the size of your volume, so worst cases situations occure when you +fill a drive with millions of < 4 ko files :p For this very special usage you +will just need a very special amount of RAM (on typical systems about +(FS size) / 256 )... On a more "normal" volume it's about +(# of files) * 20 bytes. With very few files it's about (FS Size) / 1024 / 256. + +At the beginning of the resize process, the cache is filed by scanning the FS. +The position of each extent is cut into 2 parts : high order is used as +an index into a table of pointer to a linked list which contains : +- the next ptr (sizeof struct *) +- the extent start (4 bytes) +- the extent size (4 bytes) +- number of BTree block or 0 if in prim (4 bytes) +- offset of the extent start reference + from the block beginning (2 bytes) +- sectors by BTree block, or + 1 for VH/MDB (1 byte) +- FS special file / primary structure + where the extent reference is stored (1 byte) + (3 bits for the extent index, 5 for + the actual ref) + + 0 : dont exists --- reserved + 1 : mdb / vh : catalog --- + 2 : mdb / vh : extent --- + 3 : vh : attributes X+- + 4 : vh : allocation X+- + 5 : vh : startup X+- + 6 : catalog --- + 7 : attributes X+- + 8 : extent (nothing to update) --- + 9 : extent (update catalog) --- + 10 : extent (update extent !?!) --- should not exist + 11 : extent (update attributes) X+- + 12 : extent (update allocation) X+- + 13 : extent (update startup) X+- reserved + 14 : vh : journal info block X+J + 15 : jib: journal X+J + 16 - 31 : --- + +mdb : Master Directory Block +vh : Volume Header +X+ : HFSX or HFS+ only +J : Journaled only + +Large amount of memory is allocated at once (first enough memory to fit +every files if there isn't any fragmentation +6.25%, then this value / 4, +if this wasn't enough). On a typical FS, the first allocation should be enough. + +--- diff --git a/libparted/fs/hfs/HISTORY b/libparted/fs/hfs/HISTORY new file mode 100644 index 0000000..5e138a6 --- /dev/null +++ b/libparted/fs/hfs/HISTORY @@ -0,0 +1,115 @@ +## modifications dd-mm-yyyy +---------------------- PATCH FOR PARTED 1.6.5 ---------------------------- + 1 initial revision 07-04-2003 + 2 one pass resizing, removal of debug info 08-04-2003 + 3 safe abort if resize failed, code cleanups, timer, 10-04-2003 + source file split, won't resize if not unmounted, + only relocate data if needed, minimize disk operations + 4 memory leaks removal, code cleanups, resize hfs+ code, 17-04-2003 + more checks, minor hfs resize bugfix, probe code + returns real geometry + 5 hfs+ resize bugfixes : 19-04-2003 + * fragmented fs could be corrupted + * VH wasn't written on error during alloc map writing + * attributes file could be corrupted + 6 Use PedSector to be able to use 2To+ HD 23-04-2003 + Minor probe bugfix, Cleanups, HFS+ Timer tuning, + 7 80 columns indentation 23-04-2003 + 8 Bugfix free blocks calculation in wrapper + (makes Mac OS boot !) 28-04-2003 +---------------------- PATCH FOR PARTED 1.6.6 ---------------------------- + 9 Don't destroy the file being worked on in case of + interruption of Parted 28-10-2003 +---------------------- PATCH FOR PARTED 1.6.10 --------------------------- +10 Regression tests, URL correction, In effect_move_extent : + corrected memory leak & corrected a bug in precondition checks + Added error messages, Check ped_alloc results + Use macro for test / set / clear in the allocation bitmap + Light probe correction, Check return value of get_empty_end + Moved dynamic memory allocation out of effect_move_extent + Check HFS+ version, Set implementation creator code + Check journal absence, Corrected a bug in HFS+ block number + calculation 24-04-2004 +--------------------- PATCH FOR PARTED 1.6.11 ---------------------------- +11-Some pointer dereference moved after non nul assertion + -Error messages for HFS(+) file IO + -Memory leak correction in hfs(plus)_read_bad_blocks + -Mark out of volume blocks as used + (improve compatibility with broken HFS+ Linux + implementation) + WARNING : this fix is not 100% tn1150 compatible : + "The allocation file may be larger than the minimum + number of bits required for the given volume size. + Any unused bits in the bitmap must be set to _zero_." + Anyway neither is the Linux implementation, nor was my + previous implementations + Maybe I should ask Apple to change the specifications + -HISTORY, DOC and TODO files 29-04-2004 +12 Corrected a bug in hfsplus_volume_resize : size of alloc + bitmap could be miscalculated 29-04-2004 +--------------------- PATCH FOR PARTED 1.6.12 ---------------------------- +13-Finally partial rewrite of *_search_move_* + Easier to maintain and prepare for extent search and + relocation algorithm changes for better ones. + -"An extent has not been relocated!" message now only when + relocation requested + -Slightly better and simpler relocation algorithm + -Update of Makefile.in and Makefile.am in fs_hfs + -Sign correction for some 8bits HFS integers + -Added the option --enable-hfs-extract-fs in 'configure' + -Added every ped_geometry_sync where needed + -Bugfix : "A root node does not need to exist + (if the tree is empty)." + - now handled correctly in btree_search + -Bugfix : failure wasn't detected in some cases + during 2 pass relocation (*_search_move_*) + -Bugfix : The extent key comparaison was done in a wrong order + and a pad field was used during the comparaison + -Bugfix : in hfs_file_find_sector and hfsplus_file_find_sector + the absolute position of a file sector could be + miscalculated in case of fragmentation, resulting + in potential data corruption, or various errors + -Bugfix : The end of the HFS bitmap compatibility block was + miscalculated ( (1<<16)/8 instead of (1<<16) ) + in hfs_resize + 07-09-2004 +--------------------- PATCH FOR PARTED 1.6.14 ---------------------------- +14 Port of Patch 13 for Parted 1.6.14 (update timestamps) + 08-09-2004 +--------------------- PATCH FOR PARTED 1.6.15 ---------------------------- +15-hfsplus_open : added a warning message if the "attributes" + special file exists + -hfsplus_open : added a test to check if the "allocation" + special file has been correctly opened + -optimisation of hfs+ block access : don't recalculate + the address of each sector, and avoid checking the cache if + obviously not useful + ( hfsplus_file_read && hfsplus_file_write + && hfsplus_file_find_extent && hfs_file_find_sector) + -cut the "hfs.c" file in several parts + -Bugfix: in hfsplus_do_move_primary, hfs_effect_move_extent + was called instead of hfsplus_effect_move_extent !!! + This could not produce data corruption, because of a welcome + ASSERT in *_effect_move_extent that would detect the bug :) + -Bugfix: in hfs_effect_move_extent, do + PED_ASSERT(*ptr_to_fblock <= *ptr_fblock, return -1); + instead of + PED_ASSERT(*ptr_to_fblock < *ptr_fblock, return -1); + and added that assertion to hfsplus_effect_move_extent + -Bugfix: bugs introduced in rewrite of hfsplus_file_read + && hfsplus_file_write : last sector was incorrectly detected + as out of file. + -Cache the extent references (speed improvement ?) + 23-09-2004 +16-Bugfix: in hfsplus_do_move (reloc_plus.c), case CR_BTREE_EXT_ATTR + incorrectly updated the cached part of priv_data->catalog_file + instead of priv_data->attributes_file + -Bugfix: in hfs_read_bad_blocks && hfsplus_read_bad_blocks, + now generate an error if file_ID or type mismatch after the + first pass + Also check return value of ped_malloc + -Bugfix: in hfsplus_btree_search, check return value of ped_malloc + 29-09-2004 +---------------- INTEGRATION IN PARTED 1.6.22 (cvs) ---------------------- +Futur changes will be described in ../../ChangeLog + 02-02-2005 diff --git a/libparted/fs/hfs/Makefile b/libparted/fs/hfs/Makefile new file mode 100644 index 0000000..f89fd9a --- /dev/null +++ b/libparted/fs/hfs/Makefile @@ -0,0 +1,10 @@ + +CFLAGS+= -I../include -Wall -O2 + +OBJS=advfs.o cache.o file_plus.o journal.o reloc.o advfs_plus.o file.o \ + hfs.o probe.o reloc_plus.o + +all: $(OBJS) + +clean: + rm -f $(OBJS) diff --git a/libparted/fs/hfs/TODO b/libparted/fs/hfs/TODO new file mode 100644 index 0000000..6e408e3 --- /dev/null +++ b/libparted/fs/hfs/TODO @@ -0,0 +1,27 @@ +--- TODO --- + + * Continue to write regressions tests and try on 2.6 kernel -- (high) + * Fix timer progression calculation, according to the new + caching code -- (high) + * write doc, website, ... -- (high) + * Check block allocation in linux 2.4 and remove + compatibility code if possible -- (high) + + * In hfs(plus)_btree_search , use a static variable to detect + illegal recursion and abort in that case. (find the right + number of recursion before reporting bug) -- easy -- (medium) + * Finish the HFS Extractor -- (medium) + (add mdb & vh extraction, and maybe journal) + + * Write code to allow enlarging and moving HFS[+x] -- (low) + * Use a bitmap to internaly store the bad blocks -- (low) + * Less bitmap saves ? -- (low) + * Continue to change the relocation algorithm + for a better one :) -- (low) + +--- NOT todo --- + + * debug HFS(+) Linux implementation (block allocation for HFS+, + hard and sym links for HFS+, filename length for HFS, ...) -- (dont) + /// Linux 2.6 contains HFS(+) implementations with less bugs + /// Linux 2.4 should not be used anymore to access HFS(+) diff --git a/libparted/fs/hfs/advfs.c b/libparted/fs/hfs/advfs.c new file mode 100644 index 0000000..a8adfb3 --- /dev/null +++ b/libparted/fs/hfs/advfs.c @@ -0,0 +1,328 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file.h" + +#include "advfs.h" + +/* - if a < b, 0 if a == b, + if a > b */ +/* Comparaison is done in the following order : */ +/* CNID, then fork type, then start block */ +/* Note that HFS implementation in linux has a bug */ +/* in this function */ +static int +hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b) +{ + HfsExtentKey* key1 = (HfsExtentKey*) a; + HfsExtentKey* key2 = (HfsExtentKey*) b; + + /* do NOT use a substraction, because */ + /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */ + /* would return -2, despite the fact */ + /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */ + if (key1->file_ID != key2->file_ID) + return PED_BE32_TO_CPU(key1->file_ID) < + PED_BE32_TO_CPU(key2->file_ID) ? + -1 : +1; + + if (key1->type != key2->type) + return (int)(key1->type - key2->type); + + if (key1->start == key2->start) + return 0; + /* the whole thing wont work with 16 bits ints */ + /* anyway */ + return (int)( PED_BE16_TO_CPU(key1->start) - + PED_BE16_TO_CPU(key2->start) ); +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive record_size bytes */ +/* WARNING : the compare function called only handle Extents BTree */ +/* so modify this function if you want to do lookup in */ +/* other BTrees has well */ +int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number; + int i; + + /* Read the header node */ + if (!hfs_file_read_sector(b_tree_file, node, 0)) + return 0; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT-2)))))); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU(header->root_node); + if (!node_number) + return 0; + + /* Read the root node */ + if (!hfs_file_read_sector(b_tree_file, node, node_number)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + record_key = (HfsPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); + /* check for obvious error in FS */ + if (((uint8_t*)record_key - node < HFS_FIRST_REC) + || ((uint8_t*)record_key - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + if (hfs_extent_key_cmp(record_key, key) <= 0) + break; + } + if (!i) return 0; + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = (1 + record_key->key_length + 1) & ~1; + node_number = PED_BE32_TO_CPU (*((uint32_t *) + (((uint8_t *) record_key) + skip))); + if (!hfs_file_read_sector(b_tree_file, node, + node_number)) + return 0; + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = 1; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = (uint8_t*)record_key - node; + record_ref->record_number = i; + } + + /* success */ + return 1; +} + +/* free the bad blocks linked list */ +void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first) +{ + HfsPrivateLinkExtent* next; + + while (first) { + next = first->next; + free (first); + first = next; + } +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +int +hfs_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + return 1; + + { + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + unsigned int block, last_start, first_pass = 1; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE16 (block); + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL) + || ret_key->file_ID != search.file_ID + || ret_key->type != search.type) { + if (first_pass) + break; + else + goto errbb; + } + if (PED_BE16_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE16_TO_CPU (ret_key->start); + for (i = 0; i < HFS_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPrivateLinkExtent* new_xt = + (HfsPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPrivateLinkExtent)); + if (!new_xt) + goto errbb; + new_xt->next = priv_data->bad_blocks_xtent_list; + memcpy(&(new_xt->extent), ret_cache+i, + sizeof (HfsExtDescriptor)); + priv_data->bad_blocks_xtent_list = new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE16_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + return 1;} + +errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + priv_data->bad_blocks_xtent_list=NULL; + priv_data->bad_blocks_xtent_nb=0; + return 0; +} + +/* This function check if fblock is a bad block */ +int +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int) (PED_BE16_TO_CPU ( + walk->extent.start_block) + + PED_BE16_TO_CPU ( + walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function returns the first sector of the last free block of an + HFS volume we can get after a hfs_pack_free_space_from_block call */ +/* On error this function returns 0 */ +PedSector +hfs_get_empty_end (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int block, last_bad, end_free_blocks; + + /* find the next block to the last bad block of the volume */ + if (!hfs_read_bad_blocks (fs)) + return 0; + + HfsPrivateLinkExtent* l; + last_bad = 0; + for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) { + if ((unsigned int) PED_BE16_TO_CPU (l->extent.start_block) + + PED_BE16_TO_CPU (l->extent.block_count) > last_bad) + last_bad = PED_BE16_TO_CPU (l->extent.start_block) + + PED_BE16_TO_CPU (l->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at the + end of the volume */ + block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks; + + return (PedSector) PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT); +} + +/* return the block which should be used to pack data to have at + least free fblock blocks at the end of the volume */ +unsigned int +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1; + block && fblock; + block--) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + fblock--; + } + + while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block--; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block++; + + return block; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/advfs.h b/libparted/fs/hfs/advfs.h new file mode 100644 index 0000000..aff503e --- /dev/null +++ b/libparted/fs/hfs/advfs.h @@ -0,0 +1,48 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ADVFS_H +#define _ADVFS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref); + +void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first); + +int +hfs_read_bad_blocks (const PedFileSystem *fs); + +int +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock); + +PedSector +hfs_get_empty_end (const PedFileSystem *fs); + +unsigned int +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock); + +#endif /* _ADVFS_H */ diff --git a/libparted/fs/hfs/advfs_plus.c b/libparted/fs/hfs/advfs_plus.c new file mode 100644 index 0000000..08d2f61 --- /dev/null +++ b/libparted/fs/hfs/advfs_plus.c @@ -0,0 +1,382 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs.h" +#include "file_plus.h" + +#include "advfs_plus.h" + +/* - if a < b, 0 if a == b, + if a > b */ +/* Comparaison is done in the following order : */ +/* CNID, then fork type, then start block */ +static int +hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b) +{ + HfsPExtentKey* key1 = (HfsPExtentKey*) a; + HfsPExtentKey* key2 = (HfsPExtentKey*) b; + + if (key1->file_ID != key2->file_ID) + return PED_BE32_TO_CPU(key1->file_ID) < + PED_BE32_TO_CPU(key2->file_ID) ? + -1 : +1; + + if (key1->type != key2->type) + return (int)(key1->type - key2->type); + + if (key1->start == key2->start) + return 0; + return PED_BE32_TO_CPU(key1->start) < + PED_BE32_TO_CPU(key2->start) ? + -1 : +1; +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive the whole record (key + data) */ +/* WARNING : the search function called only handle Extents BTree */ +/* so modify this function if you want to do lookup in */ +/* other BTrees has well */ +int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number, size, bsize; + int i; + + /* Read the header node */ + if (!hfsplus_file_read_sector(b_tree_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU (header->root_node); + if (!node_number) + return 0; + + /* Get the size of a node in sectors and allocate buffer */ + size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE_DEFAULT; + node = (uint8_t*) ped_malloc (bsize); + if (!node) + return 0; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + /* Read the root node */ + if (!hfsplus_file_read (b_tree_file, node, + (PedSector) node_number * size, size)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + record_key = (HfsPPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + /* check for obvious error in FS */ + if (((uint8_t*)record_key - node < HFS_FIRST_REC) + || ((uint8_t*)record_key - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return 0; + } + if (hfsplus_extent_key_cmp(record_key, key) <= 0) + break; + } + if (!i) { free (node); return 0; } + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length) + + 1 ) & ~1; + node_number = PED_BE32_TO_CPU (*((uint32_t *) + (((uint8_t *) record_key) + skip))); + if (!hfsplus_file_read(b_tree_file, node, + (PedSector) node_number * size, + size)) { + free (node); + return 0; + } + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = size; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = (uint8_t*)record_key - node; + record_ref->record_number = i; + } + + /* success */ + free (node); + return 1; +} + +/* free the bad blocks linked list */ +void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first) +{ + HfsPPrivateLinkExtent* next; + + while (first) { + next = first->next; + free (first); + first = next; + } +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +int +hfsplus_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + return 1; + + { + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + int block, first_pass = 1; + unsigned int last_start; + + search.key_length = sizeof (HfsExtentKey) - 2; + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE32 (block); + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL) + || ret_key->file_ID != search.file_ID + || ret_key->type != search.type) { + if (first_pass) + break; + else + goto errbbp; + } + if (PED_BE32_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE32_TO_CPU (ret_key->start); + for (i = 0; i < HFSP_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPPrivateLinkExtent* new_xt = + (HfsPPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPPrivateLinkExtent)); + if (!new_xt) + goto errbbp; + new_xt->next = priv_data->bad_blocks_xtent_list; + memcpy (&(new_xt->extent), ret_cache+i, + sizeof (HfsPExtDescriptor)); + priv_data->bad_blocks_xtent_list = new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE32_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + return 1;} + +errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + priv_data->bad_blocks_xtent_list=NULL; + priv_data->bad_blocks_xtent_nb=0; + return 0; +} + +/* This function check if fblock is a bad block */ +int +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int)(PED_BE32_TO_CPU ( + walk->extent.start_block) + + PED_BE32_TO_CPU (walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function returns the first sector of the last free block of + an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */ +PedSector +hfsplus_get_empty_end (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + unsigned int block, last_bad, end_free_blocks; + + /* find the next block to the last bad block of the volume */ + if (!hfsplus_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks could not be read.")); + return 0; + } + + HfsPPrivateLinkExtent* l; + last_bad = 0; + for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) { + if ((unsigned int) PED_BE32_TO_CPU (l->extent.start_block) + + PED_BE32_TO_CPU (l->extent.block_count) > last_bad) + last_bad = PED_BE32_TO_CPU (l->extent.start_block) + + PED_BE32_TO_CPU (l->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE32_TO_CPU (vh->total_blocks); + block++) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at + the end of the volume */ + block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks; + + return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); +} + +/* On error, returns 0 */ +PedSector +hfsplus_get_min_size (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedSector min_size; + + /* don't need to add anything because every sector + can be part of allocation blocks in HFS+, and + the last block _must_ be reserved */ + min_size = hfsplus_get_empty_end(fs); + if (!min_size) return 0; + + if (priv_data->wrapper) { + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block; + PedSector hgee; + hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + /* + * if hfs+ is embedded in an hfs wrapper then the new size is : + * the new size of the hfs+ volume rounded up to the size + * of hfs blocks + * + the minimum size of the hfs wrapper without any hfs+ + * modification + * - the current size of the hfs+ volume in the hfs wrapper + */ + hgee = hfs_get_empty_end(priv_data->wrapper); + if (!hgee) return 0; + min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block + + hgee + 2 + - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb + ->old_new.embedded + .location.block_count ) + * hfs_sect_block; + } + + return min_size; +} + +/* return the block which should be used to pack data to have + at least free fblock blocks at the end of the volume */ +unsigned int +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1; + block && fblock; + block--) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + fblock--; + } + + while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block--; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block++; + + return block; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/advfs_plus.h b/libparted/fs/hfs/advfs_plus.h new file mode 100644 index 0000000..fdf61c6 --- /dev/null +++ b/libparted/fs/hfs/advfs_plus.h @@ -0,0 +1,51 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ADVFS_PLUS_H +#define _ADVFS_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref); + +void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first); + +int +hfsplus_read_bad_blocks (const PedFileSystem *fs); + +int +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock); + +PedSector +hfsplus_get_empty_end (const PedFileSystem *fs); + +PedSector +hfsplus_get_min_size (const PedFileSystem *fs); + +unsigned int +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock); + +#endif /* _ADVFS_PLUS_H */ diff --git a/libparted/fs/hfs/cache.c b/libparted/fs/hfs/cache.c new file mode 100644 index 0000000..97dcca7 --- /dev/null +++ b/libparted/fs/hfs/cache.c @@ -0,0 +1,238 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "cache.h" + +static HfsCPrivateCacheTable* +hfsc_new_cachetable(unsigned int size) +{ + HfsCPrivateCacheTable* ret; + + ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret)); + if (!ret) return NULL; + + ret->next_cache = NULL; + ret->table_size = size; + ret->table_first_free = 0; + + ret->table = ped_malloc(sizeof(*ret->table)*size); + if (!ret->table) { free(ret); return NULL; } + memset(ret->table, 0, sizeof(*ret->table)*size); + + return ret; +} + +HfsCPrivateCache* +hfsc_new_cache(unsigned int block_number, unsigned int file_number) +{ + unsigned int cachetable_size, i; + HfsCPrivateCache* ret; + + ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret)); + if (!ret) return NULL; + ret->block_number = block_number; + /* following code avoid integer overflow */ + ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ? + ( block_number >> CR_SHIFT ) + 1 : + ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT + ; + + ret->linked_ref = (HfsCPrivateExtent**) + ped_malloc( sizeof(*ret->linked_ref) + * ret->linked_ref_size ); + if (!ret->linked_ref) { free(ret); return NULL; } + + cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST; + if (cachetable_size < file_number) cachetable_size = (unsigned) -1; + ret->first_cachetable_size = cachetable_size; + ret->table_list = hfsc_new_cachetable(cachetable_size); + if (!ret->table_list) { + free(ret->linked_ref); + free(ret); + return NULL; + } + ret->last_table = ret->table_list; + + for (i = 0; i < ret->linked_ref_size; ++i) + ret->linked_ref[i] = NULL; + + ret->needed_alloc_size = 0; + + return ret; +} + +static void +hfsc_delete_cachetable(HfsCPrivateCacheTable* list) +{ + HfsCPrivateCacheTable* next; + + while (list) { + free (list->table); + next = list->next_cache; + free (list); + list = next; + } +} + +void +hfsc_delete_cache(HfsCPrivateCache* cache) +{ + hfsc_delete_cachetable(cache->table_list); + free(cache->linked_ref); + free(cache); +} + +HfsCPrivateExtent* +hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, + uint32_t block, uint16_t offset, uint8_t sbb, + uint8_t where, uint8_t ref_index) +{ + HfsCPrivateExtent* ext; + unsigned int idx = start >> CR_SHIFT; + + PED_ASSERT(idx < cache->linked_ref_size); + + for (ext = cache->linked_ref[idx]; + ext && start != ext->ext_start; + ext = ext->next); + + if (ext) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to register an extent starting at block " + "0x%X, but another one already exists at this " + "position. You should check the file system!"), + start); + return NULL; + } + + if ( cache->last_table->table_first_free + == cache->last_table->table_size ) { + cache->last_table->next_cache = + hfsc_new_cachetable( ( cache->first_cachetable_size + / CR_NEW_ALLOC_DIV ) + + CR_ADD_CST ); + if (!cache->last_table->next_cache) + return NULL; + cache->last_table = cache->last_table->next_cache; + } + + ext = cache->last_table->table+(cache->last_table->table_first_free++); + + ext->ext_start = start; + ext->ext_length = length; + ext->ref_block = block; + ext->ref_offset = offset; + ext->sect_by_block = sbb; + ext->where = where; + ext->ref_index = ref_index; + + ext->next = cache->linked_ref[idx]; + cache->linked_ref[idx] = ext; + + cache->needed_alloc_size = cache->needed_alloc_size > + (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ? + cache->needed_alloc_size : + (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb; + + return ext; +} + +HfsCPrivateExtent* +hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start) +{ + HfsCPrivateExtent* ret; + unsigned int idx = start >> CR_SHIFT; + + PED_ASSERT(idx < cache->linked_ref_size); + + for (ret = cache->linked_ref[idx]; + ret && start != ret->ext_start; + ret = ret->next); + + return ret; +} + +/* Can't fail if extent begining at old_start exists */ +/* Returns 0 if no such extent, or on error */ +HfsCPrivateExtent* +hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, + uint32_t new_start) +{ + HfsCPrivateExtent** ppext; + HfsCPrivateExtent* pext; + + unsigned int idx1 = old_start >> CR_SHIFT; + unsigned int idx2 = new_start >> CR_SHIFT; + + PED_ASSERT(idx1 < cache->linked_ref_size); + PED_ASSERT(idx2 < cache->linked_ref_size); + + for (pext = cache->linked_ref[idx2]; + pext && new_start != pext->ext_start; + pext = pext->next); + + if (pext) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Trying to move an extent from block Ox%X to block " + "Ox%X, but another one already exists at this " + "position. This should not happen!"), + old_start, new_start); + return NULL; + } + + for (ppext = &(cache->linked_ref[idx1]); + (*ppext) && old_start != (*ppext)->ext_start; + ppext = &((*ppext)->next)); + + if (!(*ppext)) return NULL; + + /* removing the extent from the cache */ + pext = *ppext; + (*ppext) = pext->next; + + /* change ext_start and insert the extent again */ + pext->ext_start = new_start; + pext->next = cache->linked_ref[idx2]; + cache->linked_ref[idx2] = pext; + + return pext; +} + +#endif /* DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/cache.h b/libparted/fs/hfs/cache.h new file mode 100644 index 0000000..e0c73e0 --- /dev/null +++ b/libparted/fs/hfs/cache.h @@ -0,0 +1,117 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +/* CR => CACHE REF */ +#define CR_NULL 0 /* reserved */ +#define CR_PRIM_CAT 1 +#define CR_PRIM_EXT 2 +#define CR_PRIM_ATTR 3 +#define CR_PRIM_ALLOC 4 +#define CR_PRIM_START 5 +#define CR_BTREE_CAT 6 +#define CR_BTREE_ATTR 7 +#define CR_BTREE_EXT_0 8 +#define CR_BTREE_EXT_CAT 9 +#define CR_BTREE_EXT_EXT 10 /* should not happen ! */ +#define CR_BTREE_EXT_ATTR 11 +#define CR_BTREE_EXT_ALLOC 12 +#define CR_BTREE_EXT_START 13 /* unneeded in current code */ +#define CR_BTREE_CAT_JIB 14 /* journal info block */ +#define CR_BTREE_CAT_JL 15 /* journal */ +/* 16 -> 31 || high order bit */ /* reserved */ + +/* tuning */ +#define CR_SHIFT 8 /* number of bits to shift start_block by */ + /* to get the index of the linked list */ +#define CR_OVER_DIV 16 /* alloc a table for (1+1/CR_OVER_DIV) * + file_number + CR_ADD_CST */ +#define CR_ADD_CST 16 +#define CR_NEW_ALLOC_DIV 4 /* divide the size of the first alloc table + by this value to allocate next tables */ + +/* See DOC for an explaination of this structure */ +/* Access read only from outside cache.c */ +struct _HfsCPrivateExtent { + struct _HfsCPrivateExtent* next; + uint32_t ext_start; + uint32_t ext_length; + uint32_t ref_block; + uint16_t ref_offset; + uint8_t sect_by_block; + unsigned where : 5; + unsigned ref_index : 3; /* 0 -> 7 */ +}; +typedef struct _HfsCPrivateExtent HfsCPrivateExtent; + +/* Internaly used by cache.c for custom memory managment only */ +struct _HfsCPrivateCacheTable { + struct _HfsCPrivateCacheTable* next_cache; + HfsCPrivateExtent* table; + unsigned int table_size; + unsigned int table_first_free; + /* first_elemt ? */ +}; +typedef struct _HfsCPrivateCacheTable HfsCPrivateCacheTable; + +/* Internaly used by cache.c for custom memory managment + and cache handling only */ +struct _HfsCPrivateCache { + HfsCPrivateCacheTable* table_list; + HfsCPrivateCacheTable* last_table; + HfsCPrivateExtent** linked_ref; + unsigned int linked_ref_size; + unsigned int block_number; + unsigned int first_cachetable_size; + unsigned int needed_alloc_size; +}; +typedef struct _HfsCPrivateCache HfsCPrivateCache; + +HfsCPrivateCache* +hfsc_new_cache(unsigned int block_number, unsigned int file_number); + +void +hfsc_delete_cache(HfsCPrivateCache* cache); + +HfsCPrivateExtent* +hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, + uint32_t block, uint16_t offset, uint8_t sbb, + uint8_t where, uint8_t index); + +HfsCPrivateExtent* +hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start); + +HfsCPrivateExtent* +hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, + uint32_t new_start); + +static __inline__ unsigned int +hfsc_cache_needed_buffer(HfsCPrivateCache* cache) +{ + return cache->needed_alloc_size; +} + +#endif /* _CACHE_H */ diff --git a/libparted/fs/hfs/file.c b/libparted/fs/hfs/file.c new file mode 100644 index 0000000..9fc501b --- /dev/null +++ b/libparted/fs/hfs/file.c @@ -0,0 +1,228 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs.h" + +#include "file.h" + +/* Open the data fork of a file with its first three extents and its CNID */ +HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPrivateFile* file; + + file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile)); + if (!file) return NULL; + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS file */ +void +hfs_file_close (HfsPrivateFile* file) +{ + free (file); +} + +/* warning : only works on data forks */ +static int +hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block, + HfsExtDataRec cache, uint16_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE16 (block); + + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsExtDataRec)); + *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start); + + return 1; +} + +/* find and return the nth sector of a file */ +/* return 0 on error */ +static PedSector +hfs_file_find_sector (HfsPrivateFile* file, PedSector sector) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + unsigned int sect_by_block = PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + unsigned int i, s, vol_block; + unsigned int block = sector / sect_by_block; + unsigned int offset = sector % sect_by_block; + + /* in the three first extent */ + for (s = 0, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && ( block < s + PED_BE16_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->first[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->first[i].block_count); + } + + /* in the three cached extent */ + if (file->start_cache && block >= file->start_cache) + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->cache[i].block_count); + } + + /* update cache */ + if (!hfs_get_extent_containing (file, block, file->cache, + &(file->start_cache))) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_CANCEL, + _("Could not update the extent cache for HFS file with " + "CNID %X."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + /* in the three cached extent */ + PED_ASSERT(file->start_cache && block >= file->start_cache); + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->cache[i].block_count); + } + + return 0; + + sector_found: + return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block) + + (PedSector) vol_block * sect_by_block + + offset; +} + +/* Read the nth sector of a file */ +/* return 0 on error */ +int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to read HFS file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + abs_sector = hfs_file_find_sector (file, sector); + if (!abs_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS file with " + "CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + return ped_geometry_read (file->fs->geom, buf, abs_sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to write HFS file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + abs_sector = hfs_file_find_sector (file, sector); + if (!abs_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS file with " + "CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + return ped_geometry_write (file->fs->geom, buf, abs_sector, 1); +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/file.h b/libparted/fs/hfs/file.h new file mode 100644 index 0000000..a16f835 --- /dev/null +++ b/libparted/fs/hfs/file.h @@ -0,0 +1,41 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _FILE_H +#define _FILE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb); + +void +hfs_file_close (HfsPrivateFile* file); + +int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +#endif /* _FILE_H */ diff --git a/libparted/fs/hfs/file_plus.c b/libparted/fs/hfs/file_plus.c new file mode 100644 index 0000000..acacc93 --- /dev/null +++ b/libparted/fs/hfs/file_plus.c @@ -0,0 +1,273 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs_plus.h" + +#include "file_plus.h" + +/* Open the data fork of a file with its first eight extents and its CNID */ +/* CNID and ext_desc must be in disc order, sect_nb in CPU order */ +/* return null on failure */ +HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPPrivateFile* file; + + file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile)); + if (!file) return NULL; + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS+ file */ +void +hfsplus_file_close (HfsPPrivateFile* file) +{ + free (file); +} + +/* warning : only works on data forks */ +static int +hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block, + HfsPExtDataRec cache, uint32_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + + search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2); + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE32 (block); + + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsPExtDataRec)); + *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start); + + return 1; +} + +/* find a sub extent contained in the desired area */ +/* and with the same starting point */ +/* return 0 in sector_count on error, or the physical area */ +/* on the volume corresponding to the logical area in the file */ +static HfsPPrivateExtent +hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent ret = {0,0}; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + unsigned int sect_by_block = PED_BE32_TO_CPU ( + priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT; + unsigned int i, s, vol_block, size; + PedSector sect_size; + unsigned int block = sector / sect_by_block; + unsigned int offset = sector % sect_by_block; + + /* in the 8 first extent */ + for (s = 0, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->first[i] + .start_block); + size = PED_BE32_TO_CPU (file->first[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->first[i].block_count); + } + + /* in the 8 cached extent */ + if (file->start_cache && block >= file->start_cache) + for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + size = PED_BE32_TO_CPU (file->cache[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->cache[i].block_count); + } + + /* update cache */ + if (!hfsplus_get_extent_containing (file, block, file->cache, + &(file->start_cache))) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_CANCEL, + _("Could not update the extent cache for HFS+ file " + "with CNID %X."), + PED_BE32_TO_CPU(file->CNID)); + return ret; /* ret == {0,0} */ + } + + /* ret == {0,0} */ + PED_ASSERT(file->start_cache && block >= file->start_cache); + + for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + size = PED_BE32_TO_CPU (file->cache[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->cache[i].block_count); + } + + return ret; + +plus_sector_found: + sect_size = (PedSector) size * sect_by_block - offset; + ret.start_sector = vol_block * sect_by_block + offset; + ret.sector_count = (sect_size < nb) ? sect_size : nb; + return ret; +} + +int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent phy_area; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + char *b = buf; + + if (sector+nb < sector /* detect overflow */ + || sector+nb > file->sect_nb) /* out of file */ { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to read HFS+ file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + while (nb) { + phy_area = hfsplus_file_find_extent(file, sector, nb); + if (phy_area.sector_count == 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS+ file " + "with CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + if (!ped_geometry_read(priv_data->plus_geom, b, + phy_area.start_sector, + phy_area.sector_count)) + return 0; + + nb -= phy_area.sector_count; /* < nb anyway ... */ + sector += phy_area.sector_count; + b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; + } + + return 1; +} + +int +hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent phy_area; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + char *b = buf; + + if (sector+nb < sector /* detect overflow */ + || sector+nb > file->sect_nb) /* out of file */ { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to write HFS+ file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + while (nb) { + phy_area = hfsplus_file_find_extent(file, sector, nb); + if (phy_area.sector_count == 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS+ file " + "with CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + if (!ped_geometry_write(priv_data->plus_geom, b, + phy_area.start_sector, + phy_area.sector_count)) + return 0; + + nb -= phy_area.sector_count; /* < nb anyway ... */ + sector += phy_area.sector_count; + b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; + } + + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/file_plus.h b/libparted/fs/hfs/file_plus.h new file mode 100644 index 0000000..e2dcbd2 --- /dev/null +++ b/libparted/fs/hfs/file_plus.h @@ -0,0 +1,60 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _FILE_PLUS_H +#define _FILE_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb); + +void +hfsplus_file_close (HfsPPrivateFile* file); + +int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +int +hfsplus_file_write(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +/* Read the nth sector of a file */ +/* return 0 on error */ +static __inline__ int +hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + return hfsplus_file_read(file, buf, sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +static __inline__ int +hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + return hfsplus_file_write(file, buf, sector, 1); +} + + +#endif /* _FILE_PLUS_H */ diff --git a/libparted/fs/hfs/hfs.c b/libparted/fs/hfs/hfs.c new file mode 100644 index 0000000..51aec12 --- /dev/null +++ b/libparted/fs/hfs/hfs.c @@ -0,0 +1,1354 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2003-2005, 2007, 2009-2011 Free Software Foundation, + Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + Author : Guillaume Knispel <k_guillaume@libertysurf.fr> + Report bug to <bug-parted@gnu.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "probe.h" + +uint8_t* hfs_block = NULL; +uint8_t* hfsp_block = NULL; +unsigned hfs_block_count; +unsigned hfsp_block_count; + +#define HFS_BLOCK_SIZES ((int[2]){512, 0}) +#define HFSP_BLOCK_SIZES ((int[2]){512, 0}) +#define HFSX_BLOCK_SIZES ((int[2]){512, 0}) + +#ifndef DISCOVER_ONLY +#include "file.h" +#include "reloc.h" +#include "advfs.h" + +static PedFileSystemType hfs_type; +static PedFileSystemType hfsplus_type; + + +/* ----- HFS ----- */ + +/* This is a very unundoable operation */ +/* Maybe I shouldn't touch the alternate MDB ? */ +/* Anyway clobber is call before other fs creation */ +/* So this is a non-issue */ +static int +hfs_clobber (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + + memset (buf, 0, PED_SECTOR_SIZE_DEFAULT); + + /* destroy boot blocks, mdb, alternate mdb ... */ + return (!!ped_geometry_write (geom, buf, 0, 1)) & + (!!ped_geometry_write (geom, buf, 1, 1)) & + (!!ped_geometry_write (geom, buf, 2, 1)) & + (!!ped_geometry_write (geom, buf, geom->length - 2, 1)) & + (!!ped_geometry_write (geom, buf, geom->length - 1, 1)) & + (!!ped_geometry_sync (geom)); +} + +static PedFileSystem* +hfs_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedFileSystem* fs; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFSData* priv_data; + + if (!hfsc_can_use_geom (geom)) + return NULL; + + /* Read MDB */ + if (!ped_geometry_read (geom, buf, 2, 1)) + return NULL; + + /* Allocate memory */ + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto ho; + mdb = (HfsMasterDirectoryBlock*) + ped_malloc (sizeof (HfsMasterDirectoryBlock)); + if (!mdb) goto ho_fs; + priv_data = (HfsPrivateFSData*) + ped_malloc (sizeof (HfsPrivateFSData)); + if (!priv_data) goto ho_mdb; + + memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock)); + + /* init structures */ + priv_data->mdb = mdb; + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extent_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + mdb->extents_file_rec, + PED_CPU_TO_BE32 (mdb->extents_file_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->extent_file) goto ho_pd; + priv_data->catalog_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + mdb->catalog_file_rec, + PED_CPU_TO_BE32 (mdb->catalog_file_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->catalog_file) goto ho_ce; + /* Read allocation blocks */ + if (!ped_geometry_read(geom, priv_data->alloc_map, + PED_BE16_TO_CPU (mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8) ) ) + goto ho_cf; + + fs->type = &hfs_type; + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) goto ho_cf; + fs->type_specific = (void*) priv_data; + fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes) + >> HFS_UNMOUNTED ) & 1; + + return fs; + +/*--- clean error handling ---*/ +ho_cf: hfs_file_close(priv_data->catalog_file); +ho_ce: hfs_file_close(priv_data->extent_file); +ho_pd: free(priv_data); +ho_mdb: free(mdb); +ho_fs: free(fs); +ho: return NULL; +} + +static int +hfs_close (PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; + + hfs_file_close (priv_data->extent_file); + hfs_file_close (priv_data->catalog_file); + if (priv_data->bad_blocks_loaded) + hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list); + free (priv_data->mdb); + free (priv_data); + ped_geometry_destroy (fs->geom); + free (fs); + + return 1; +} + +static PedConstraint* +hfs_get_resize_constraint (const PedFileSystem *fs) +{ + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + /* 2 = last two sectors (alternate MDB and unused sector) */ + min_size = hfs_get_empty_end(fs) + 2; + if (min_size == 2) return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +static int +hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + unsigned int nblock, nfree; + unsigned int block, to_free; + HfsPrivateFSData* priv_data; + HfsMasterDirectoryBlock* mdb; + int resize = 1; + unsigned int hfs_sect_block; + PedSector hgee; + + /* check preconditions */ + PED_ASSERT (fs != NULL); + PED_ASSERT (fs->geom != NULL); + PED_ASSERT (geom != NULL); +#ifdef DEBUG + PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0); +#else + if ((hgee = hfs_get_empty_end(fs)) == 0) + return 0; +#endif + + PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0); + + if (ped_geometry_test_equal(fs->geom, geom)) + return 1; + + priv_data = (HfsPrivateFSData*) fs->type_specific; + mdb = priv_data->mdb; + hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + + if (fs->geom->start != geom->start + || geom->length > fs->geom->length + || geom->length < hgee + 2) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, HFS cannot be resized that way yet.")); + return 0; + } + + /* Flush caches */ + if (!ped_geometry_sync(fs->geom)) + return 0; + + /* Clear the unmounted bit */ + mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED )); + if (!ped_geometry_read (fs->geom, buf, 2, 1)) + return 0; + memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock)); + if ( !ped_geometry_write (fs->geom, buf, 2, 1) + || !ped_geometry_sync (fs->geom)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + to_free = ( fs->geom->length - geom->length + + hfs_sect_block - 1 ) + / hfs_sect_block ; + block = hfs_find_start_pack (fs, to_free); + if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation has failed.")); + goto write_MDB; + } + + /* Calculate new block number and other MDB field */ + nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) ) + / hfs_sect_block; + nfree = PED_BE16_TO_CPU (mdb->free_blocks) + - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock ); + + /* Check that all block after future end are really free */ + for (block = nblock; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation left some data in the end " + "of the volume.")); + goto write_MDB; + } + } + + /* Mark out of volume blocks as used + (broken implementations compatibility) */ + for ( block = nblock; block < (1 << 16); ++block) + SET_BLOC_OCCUPATION(priv_data->alloc_map,block); + + /* save the allocation map + I do not write until start of allocation blocks + but only until pre-resize end of bitmap blocks + because the specifications do _not_ assert that everything + until allocation blocks is boot, mdb and alloc */ + ped_geometry_write(fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1) + / (PED_SECTOR_SIZE_DEFAULT * 8)); + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock) + mdb->next_allocation = PED_CPU_TO_BE16 (0); + mdb->total_blocks = PED_CPU_TO_BE16 (nblock); + mdb->free_blocks = PED_CPU_TO_BE16 (nfree); + /* update parted structure */ + fs->geom->length = geom->length; + fs->geom->end = fs->geom->start + geom->length - 1; + } + + /* Set the unmounted bit */ + mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED ); + + /* Effective write */ + write_MDB: + ped_timer_set_state_name(timer,_("writing HFS Master Directory Block")); + + if (!hfs_update_mdb(fs)) { + ped_geometry_sync(geom); + return 0; + } + + if (!ped_geometry_sync(geom)) + return 0; + + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* ----- HFS+ ----- */ + +#include "file_plus.h" +#include "advfs_plus.h" +#include "reloc_plus.h" +#include "journal.h" + +static int +hfsplus_clobber (PedGeometry* geom) +{ + unsigned int i = 1; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + HfsMasterDirectoryBlock *mdb; + + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1)) + return 0; + + if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) { + /* embedded hfs+ */ + PedGeometry *embedded; + + i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT; + embedded = ped_geometry_new ( + geom->dev, + (PedSector) geom->start + + PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + mdb->old_new.embedded.location.start_block ) * i, + (PedSector) PED_BE16_TO_CPU ( + mdb->old_new.embedded.location.block_count ) * i ); + if (!embedded) i = 0; + else { + i = hfs_clobber (embedded); + ped_geometry_destroy (embedded); + } + } + + /* non-embedded or envelop destroy as hfs */ + return ( hfs_clobber (geom) && i ); +} + +static int +hfsplus_close (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + free(priv_data->alloc_map); + free(priv_data->dirty_alloc_map); + hfsplus_file_close (priv_data->allocation_file); + hfsplus_file_close (priv_data->attributes_file); + hfsplus_file_close (priv_data->catalog_file); + hfsplus_file_close (priv_data->extents_file); + if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); + if (priv_data->wrapper) hfs_close(priv_data->wrapper); + ped_geometry_destroy (fs->geom); + free(priv_data->vh); + free(priv_data); + free(fs); + + return 1; +} + +static PedFileSystem* +hfsplus_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedFileSystem* fs; + HfsPVolumeHeader* vh; + HfsPPrivateFSData* priv_data; + PedGeometry* wrapper_geom; + unsigned int map_sectors; + + if (!hfsc_can_use_geom (geom)) + return NULL; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto hpo; + vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader)); + if (!vh) goto hpo_fs; + priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData)); + if (!priv_data) goto hpo_vh; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) goto hpo_pd; + fs->type_specific = (void*) priv_data; + + if ((wrapper_geom = hfs_and_wrapper_probe (geom))) { + HfsPrivateFSData* hfs_priv_data; + PedSector abs_sect, length; + unsigned int bs; + + ped_geometry_destroy (wrapper_geom); + priv_data->wrapper = hfs_open(geom); + if (!priv_data->wrapper) goto hpo_gm; + hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + abs_sect = (PedSector) geom->start + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.start_block ) + * bs; + length = (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count) + * bs; + priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect, + length); + if (!priv_data->plus_geom) goto hpo_wr; + priv_data->free_geom = 1; + } else { + priv_data->wrapper = NULL; + priv_data->plus_geom = fs->geom; + priv_data->free_geom = 0; + } + + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg; + memcpy (vh, buf, sizeof (HfsPVolumeHeader)); + priv_data->vh = vh; + + if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE) + && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("No valid HFS[+X] signature has been found while " + "opening.")); + goto hpo_pg; + } + + if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE) + && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) { + if (ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_IGNORE_CANCEL, + _("Version %d of HFS+ isn't supported."), + PED_BE16_TO_CPU(vh->version)) + != PED_EXCEPTION_IGNORE) + goto hpo_pg; + } + + if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE) + && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) { + if (ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_IGNORE_CANCEL, + _("Version %d of HFSX isn't supported."), + PED_BE16_TO_CPU(vh->version)) + != PED_EXCEPTION_IGNORE) + goto hpo_pg; + } + + priv_data->jib_start_block = 0; + priv_data->jl_start_block = 0; + if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) { + if (!hfsj_replay_journal(fs)) + goto hpo_pg; + } + + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extents_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + vh->extents_file.extents, + PED_BE64_TO_CPU ( + vh->extents_file.logical_size ) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->extents_file) goto hpo_pg; + priv_data->catalog_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + vh->catalog_file.extents, + PED_BE64_TO_CPU ( + vh->catalog_file.logical_size ) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->catalog_file) goto hpo_ce; + priv_data->attributes_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID), + vh->attributes_file.extents, + PED_BE64_TO_CPU ( + vh->attributes_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->attributes_file) goto hpo_cc; + + map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + priv_data->dirty_alloc_map = (uint8_t*) + ped_malloc ((map_sectors + 7) / 8); + if (!priv_data->dirty_alloc_map) goto hpo_cl; + memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8); + priv_data->alloc_map = (uint8_t*) + ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->alloc_map) goto hpo_dm; + + priv_data->allocation_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID), + vh->allocation_file.extents, + PED_BE64_TO_CPU ( + vh->allocation_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->allocation_file) goto hpo_am; + if (!hfsplus_file_read (priv_data->allocation_file, + priv_data->alloc_map, 0, map_sectors)) { + hfsplus_close(fs); + return NULL; + } + + fs->type = &hfsplus_type; + fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1) + && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1); + + return fs; + +/*--- clean error handling ---*/ +hpo_am: free(priv_data->alloc_map); +hpo_dm: free(priv_data->dirty_alloc_map); +hpo_cl: hfsplus_file_close (priv_data->attributes_file); +hpo_cc: hfsplus_file_close (priv_data->catalog_file); +hpo_ce: hfsplus_file_close (priv_data->extents_file); +hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); +hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper); +hpo_gm: ped_geometry_destroy (fs->geom); +hpo_pd: free(priv_data); +hpo_vh: free(vh); +hpo_fs: free(fs); +hpo: return NULL; +} + +static PedConstraint* +hfsplus_get_resize_constraint (const PedFileSystem *fs) +{ + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + min_size = hfsplus_get_min_size (fs); + if (!min_size) return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +static int +hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + unsigned int nblock, nfree, mblock; + unsigned int block, to_free, old_blocks; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + int resize = 1; + unsigned int hfsp_sect_block = + ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); + unsigned int map_sectors; + + old_blocks = PED_BE32_TO_CPU (vh->total_blocks); + + /* Flush caches */ + if (!ped_geometry_sync(priv_data->plus_geom)) + return 0; + + /* Clear the unmounted bit */ + /* and set the implementation code (Apple Creator Code) */ + vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED )); + vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk); + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) + return 0; + memcpy (buf, vh, sizeof (HfsPVolumeHeader)); + if ( !ped_geometry_write (priv_data->plus_geom, buf, 2, 1) + || !ped_geometry_sync (priv_data->plus_geom)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + to_free = ( priv_data->plus_geom->length + - geom->length + hfsp_sect_block + - 1 ) / hfsp_sect_block; + block = hfsplus_find_start_pack (fs, to_free); + if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation has failed.")); + goto write_VH; + } + + /* Calculate new block number and other VH field */ + /* nblock must be rounded _down_ */ + nblock = geom->length / hfsp_sect_block; + nfree = PED_BE32_TO_CPU (vh->free_blocks) + - (old_blocks - nblock); + /* free block readjustement is only needed when incorrect nblock + was used by my previous implementation, so detect the case */ + if (priv_data->plus_geom->length < old_blocks + * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT) ) { + if (priv_data->plus_geom->length % hfsp_sect_block == 1) + nfree++; + } + + /* Check that all block after future end are really free */ + mblock = ( priv_data->plus_geom->length - 2 ) + / hfsp_sect_block; + if (mblock > old_blocks - 1) + mblock = old_blocks - 1; + for ( block = nblock; + block < mblock; + block++ ) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation left some data at the end " + "of the volume.")); + goto write_VH; + } + } + + /* Mark out of volume blocks as used */ + map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8) ) + * (PED_SECTOR_SIZE_DEFAULT * 8); + for ( block = nblock; block < map_sectors; ++block) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock) + vh->next_allocation = PED_CPU_TO_BE32 (0); + vh->total_blocks = PED_CPU_TO_BE32 (nblock); + vh->free_blocks = PED_CPU_TO_BE32 (nfree); + /* update parted structure */ + priv_data->plus_geom->length = geom->length; + priv_data->plus_geom->end = priv_data->plus_geom->start + + geom->length - 1; + } + + /* Effective write */ + write_VH: + /* lasts two sectors are allocated by the alternate VH + and a reserved sector, and last block is always reserved */ + block = (priv_data->plus_geom->length - 1) / hfsp_sect_block; + if (block < PED_BE32_TO_CPU (vh->total_blocks)) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + block = (priv_data->plus_geom->length - 2) / hfsp_sect_block; + if (block < PED_BE32_TO_CPU (vh->total_blocks)) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + SET_BLOC_OCCUPATION(priv_data->alloc_map, + PED_BE32_TO_CPU (vh->total_blocks) - 1); + + /* Write the _old_ area to set out of volume blocks as used */ + map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + if (!hfsplus_file_write (priv_data->allocation_file, + priv_data->alloc_map, 0, map_sectors)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Error while writing the allocation file.")); + } else { + /* Write remaining part of allocation bitmap */ + /* This is necessary to handle pre patch-11 and third party */ + /* implementations */ + memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT); + for (block = map_sectors; + block < priv_data->allocation_file->sect_nb; + ++block) { + if (!hfsplus_file_write_sector ( + priv_data->allocation_file, + buf, block)) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Error while writing the " + "compatibility part of the " + "allocation file.")); + break; + } + } + } + ped_geometry_sync (priv_data->plus_geom); + + if (resize) { + /* Set the unmounted bit and clear the inconsistent bit */ + vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED ); + vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT ); + } + + ped_timer_set_state_name(timer, _("writing HFS+ Volume Header")); + if (!hfsplus_update_vh(fs)) { + ped_geometry_sync(priv_data->plus_geom); + return 0; + } + + if (!ped_geometry_sync(priv_data->plus_geom)) + return 0; + + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* Update the HFS wrapper mdb and bad blocks file to reflect + the new geometry of the embedded HFS+ volume */ +static int +hfsplus_wrapper_update (PedFileSystem* fs) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsCPrivateLeafRec ref; + HfsExtentKey key; + HfsNodeDescriptor* node_desc = (HfsNodeDescriptor*) node; + HfsExtentKey* ret_key; + HfsExtDescriptor* ret_data; + unsigned int i; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT ; + PedSector hfsplus_sect = (PedSector) + PED_BE32_TO_CPU (priv_data->vh->total_blocks) + * ( PED_BE32_TO_CPU (priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); + unsigned int hfs_blocks_embedded = + (hfsplus_sect + hfs_sect_block - 1) + / hfs_sect_block; + unsigned int hfs_blocks_embedded_old; + + /* update HFS wrapper MDB */ + hfs_blocks_embedded_old = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count ); + hfs_priv_data->mdb->old_new.embedded.location.block_count = + PED_CPU_TO_BE16 (hfs_blocks_embedded); + /* maybe macOS will boot with this */ + /* update : yes it does \o/ :) */ + hfs_priv_data->mdb->free_blocks = + PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks) + + hfs_blocks_embedded_old + - hfs_blocks_embedded ); + + if (!hfs_update_mdb(priv_data->wrapper)) + return 0; + + /* force reload bad block list */ + if (hfs_priv_data->bad_blocks_loaded) { + hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list); + hfs_priv_data->bad_blocks_xtent_list = NULL; + hfs_priv_data->bad_blocks_xtent_nb = 0; + hfs_priv_data->bad_blocks_loaded = 0; + } + + /* clean HFS wrapper allocation map */ + for (i = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded; + i < PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded_old; + i++ ) { + CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i); + } + /* and save it */ + if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map, + PED_BE16_TO_CPU ( + hfs_priv_data->mdb->volume_bitmap_block ), + ( PED_BE16_TO_CPU ( + hfs_priv_data->mdb->total_blocks ) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8))) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + + /* search and update the bad blocks file */ + key.key_length = sizeof(key) - 1; + key.type = HFS_DATA_FORK; + key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + key.start = 0; + if (!hfs_btree_search (hfs_priv_data->extent_file, + (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred while looking for the mandatory " + "bad blocks file.")); + return 0; + } + if (!hfs_file_read_sector (hfs_priv_data->extent_file, node, + ref.node_number)) + return 0; + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + + while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) { + for (i = 0; i < HFS_EXT_NB; i++) { + if ( ret_data[i].start_block + == hfs_priv_data->mdb->old_new + .embedded.location.start_block) { + ret_data[i].block_count = + hfs_priv_data->mdb->old_new + .embedded.location.block_count; + /* found ! : update */ + if (!hfs_file_write_sector ( + hfs_priv_data->extent_file, + node, ref.node_number) + || !ped_geometry_sync(fs->geom)) + return 0; + return 1; + } + } + + if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) { + ref.record_number++; + } else { + ref.node_number = PED_BE32_TO_CPU (node_desc->next); + if (!ref.node_number + || !hfs_file_read_sector(hfs_priv_data->extent_file, + node, ref.node_number)) + goto bb_not_found; + ref.record_number = 1; + } + + ref.record_pos = + PED_BE16_TO_CPU (*((uint16_t *) + (node + (PED_SECTOR_SIZE_DEFAULT + - 2*ref.record_number)))); + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + } + +bb_not_found: + /* not found : not a valid hfs+ wrapper : failure */ + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("It seems there is an error in the HFS wrapper: the bad " + "blocks file doesn't contain the embedded HFS+ volume.")); + return 0; +} + +static int +hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data; + PedTimer* timer_plus; + PedGeometry* embedded_geom; + PedSector hgms; + + /* check preconditions */ + PED_ASSERT (fs != NULL); + PED_ASSERT (fs->geom != NULL); + PED_ASSERT (geom != NULL); + PED_ASSERT (fs->geom->dev == geom->dev); +#ifdef DEBUG + PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0); +#else + if ((hgms = hfsplus_get_min_size (fs)) == 0) + return 0; +#endif + + if (ped_geometry_test_equal(fs->geom, geom)) + return 1; + + priv_data = (HfsPPrivateFSData*) fs->type_specific; + + if (fs->geom->start != geom->start + || geom->length > fs->geom->length + || geom->length < hgms) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, HFS+ cannot be resized that way yet.")); + return 0; + } + + if (priv_data->wrapper) { + PedSector red, hgee; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + + /* There is a wrapper so we must calculate the new geometry + of the embedded HFS+ volume */ + red = ( (fs->geom->length - geom->length + hfs_sect_block - 1) + / hfs_sect_block ) * hfs_sect_block; + /* Can't we shrink the hfs+ volume by the desired size ? */ + hgee = hfsplus_get_empty_end (fs); + if (!hgee) return 0; + if (red > priv_data->plus_geom->length - hgee) { + /* No, shrink hfs+ by the greatest possible value */ + hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block; + red = priv_data->plus_geom->length - hgee; + } + embedded_geom = ped_geometry_new (geom->dev, + priv_data->plus_geom->start, + priv_data->plus_geom->length + - red); + + /* There is a wrapper so the resize process is a two stages + process (embedded resizing then wrapper resizing) : + we create a sub timer */ + ped_timer_reset (timer); + ped_timer_set_state_name (timer, + _("shrinking embedded HFS+ volume")); + ped_timer_update(timer, 0.0); + timer_plus = ped_timer_new_nested (timer, 0.98); + } else { + /* No wrapper : the desired geometry is the desired + HFS+ volume geometry */ + embedded_geom = geom; + timer_plus = timer; + } + + /* Resize the HFS+ volume */ + if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) { + if (timer_plus != timer) ped_timer_destroy_nested (timer_plus); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Resizing the HFS+ volume has failed.")); + return 0; + } + + if (priv_data->wrapper) { + ped_geometry_destroy (embedded_geom); + ped_timer_destroy_nested (timer_plus); + ped_timer_set_state_name(timer, _("shrinking HFS wrapper")); + timer_plus = ped_timer_new_nested (timer, 0.02); + /* There's a wrapper : second stage = resizing it */ + if (!hfsplus_wrapper_update (fs) + || !hfs_resize (priv_data->wrapper, geom, timer_plus)) { + ped_timer_destroy_nested (timer_plus); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Updating the HFS wrapper has failed.")); + return 0; + } + ped_timer_destroy_nested (timer_plus); + } + ped_timer_update(timer, 1.0); + + return 1; +} + +#ifdef HFS_EXTRACT_FS +/* The following is for debugging purpose only, NOT for packaging */ + +#include <stdio.h> + +uint8_t* extract_buffer = NULL; + +static int +hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file) +{ + FILE* fout; + PedSector sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (sect = 0; sect < hfs_file->sect_nb; ++sect) { + if (!hfs_file_read_sector(hfs_file, extract_buffer, sect)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + } + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract_bitmap(const char* filename, PedFileSystem* fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int count; + FILE* fout; + PedSector sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block); + sect < PED_BE16_TO_CPU(mdb->start_block); + sect += count) { + uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block); + count = (st_block-sect) < BLOCK_MAX_BUFF ? + (st_block-sect) : BLOCK_MAX_BUFF; + if (!ped_geometry_read(fs->geom, extract_buffer, sect, count)) + goto err_close; + if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT, + 1, fout)) + goto err_close; + } + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract_mdb (const char* filename, PedFileSystem* fs) +{ + FILE* fout; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract (PedFileSystem* fs, PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This is not a real %s check. This is going to extract " + "special low level files for debugging purposes."), + "HFS"); + + extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); + if (!extract_buffer) return 0; + + hfs_extract_mdb(HFS_MDB_FILENAME, fs); + hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file); + hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file); + hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs); + + free(extract_buffer); extract_buffer = NULL; + return 0; /* nothing has been fixed by us ! */ +} + +static int +hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file) +{ + FILE* fout; + unsigned int cp_sect; + PedSector rem_sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) { + cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF; + if (!hfsplus_file_read(hfsp_file, extract_buffer, + hfsp_file->sect_nb - rem_sect, cp_sect)) + goto err_close; + if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT, + 1, fout)) + goto err_close; + } + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfsplus_extract_vh (const char* filename, PedFileSystem* fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + FILE* fout; + PedGeometry* geom = priv_data->plus_geom; + + + fout = fopen(filename, "w"); + if (!fout) return 0; + + if (!ped_geometry_read(geom, extract_buffer, 2, 1)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +/* TODO : use the timer to report what is happening */ +/* TODO : use exceptions to report errors */ +static int +hfsplus_extract (PedFileSystem* fs, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsPPrivateFile* startup_file; + + if (priv_data->wrapper) { + /* TODO : create nested timer */ + hfs_extract (priv_data->wrapper, timer); + } + + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This is not a real %s check. This is going to extract " + "special low level files for debugging purposes."), + "HFS+"); + + extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); + if (!extract_buffer) return 0; + + hfsplus_extract_vh(HFSP_VH_FILENAME, fs); + hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file); + hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file); + hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file); + hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file); + + startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID), + vh->startup_file.extents, + PED_BE64_TO_CPU ( + vh->startup_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (startup_file) { + hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file); + hfsplus_file_close(startup_file); startup_file = NULL; + } + + free(extract_buffer); extract_buffer = NULL; + return 0; /* nothing has been fixed by us ! */ +} +#endif /* HFS_EXTRACT_FS */ + +#endif /* !DISCOVER_ONLY */ + +static PedFileSystemOps hfs_ops = { + probe: hfs_probe, +#ifndef DISCOVER_ONLY + clobber: hfs_clobber, + open: hfs_open, + create: NULL, + close: hfs_close, +#ifndef HFS_EXTRACT_FS + check: NULL, +#else + check: hfs_extract, +#endif + copy: NULL, + resize: hfs_resize, + get_create_constraint: NULL, + get_resize_constraint: hfs_get_resize_constraint, + get_copy_constraint: NULL, +#else /* DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* DISCOVER_ONLY */ +}; + +static PedFileSystemOps hfsplus_ops = { + probe: hfsplus_probe, +#ifndef DISCOVER_ONLY + clobber: hfsplus_clobber, + open: hfsplus_open, + create: NULL, + close: hfsplus_close, +#ifndef HFS_EXTRACT_FS + check: NULL, +#else + check: hfsplus_extract, +#endif + copy: NULL, + resize: hfsplus_resize, + get_create_constraint: NULL, + get_resize_constraint: hfsplus_get_resize_constraint, + get_copy_constraint: NULL, +#else /* DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* DISCOVER_ONLY */ +}; + +static PedFileSystemOps hfsx_ops = { + probe: hfsx_probe, +#ifndef DISCOVER_ONLY + clobber: hfs_clobber, /* NOT hfsplus_clobber ! + HFSX can't be embedded */ + open: hfsplus_open, + create: NULL, + close: hfsplus_close, +#ifndef HFS_EXTRACT_FS + check: NULL, +#else + check: hfsplus_extract, +#endif + copy: NULL, + resize: hfsplus_resize, + get_create_constraint: NULL, + get_resize_constraint: hfsplus_get_resize_constraint, + get_copy_constraint: NULL, +#else /* DISCOVER_ONLY */ + clobber: NULL, + open: NULL, + create: NULL, + close: NULL, + check: NULL, + copy: NULL, + resize: NULL, + get_create_constraint: NULL, + get_resize_constraint: NULL, + get_copy_constraint: NULL, +#endif /* DISCOVER_ONLY */ +}; + + +static PedFileSystemType hfs_type = { + next: NULL, + ops: &hfs_ops, + name: "hfs", + block_sizes: HFS_BLOCK_SIZES +}; + +static PedFileSystemType hfsplus_type = { + next: NULL, + ops: &hfsplus_ops, + name: "hfs+", + block_sizes: HFSP_BLOCK_SIZES +}; + +static PedFileSystemType hfsx_type = { + next: NULL, + ops: &hfsx_ops, + name: "hfsx", + block_sizes: HFSX_BLOCK_SIZES +}; + +void +ped_file_system_hfs_init () +{ + ped_file_system_type_register (&hfs_type); + ped_file_system_type_register (&hfsplus_type); + ped_file_system_type_register (&hfsx_type); +} + +void +ped_file_system_hfs_done () +{ + ped_file_system_type_unregister (&hfs_type); + ped_file_system_type_unregister (&hfsplus_type); + ped_file_system_type_unregister (&hfsx_type); +} diff --git a/libparted/fs/hfs/hfs.h b/libparted/fs/hfs/hfs.h new file mode 100644 index 0000000..fe5de2c --- /dev/null +++ b/libparted/fs/hfs/hfs.h @@ -0,0 +1,647 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2003-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _HFS_H +#define _HFS_H + +/* WARNING : bn is used 2 times in theses macro */ +/* so _never_ use side effect operators when using them */ +#define TST_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) & (1<<(7-((bn)&7)))) +#define SET_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) |= (1<<(7-((bn)&7)))) +#define CLR_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7)))) + +/* Maximum number of blocks for the copy buffers */ +#define BLOCK_MAX_BUFF 256 +/* Maximum size of the copy buffers, in bytes */ +#define BYTES_MAX_BUFF 8388608 + +/* Apple Creator Codes follow */ +#define HFSP_IMPL_Shnk 0x53686e6b /* in use */ +#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */ +#define HFSP_IMPL_Resz 0x5265737a /* reserved */ +#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */ +#define HFSP_IMPL_traP 0x74726150 /* reserved */ +#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */ + +#define HFS_SIGNATURE 0x4244 /* 'BD' */ +#define HFSP_SIGNATURE 0x482B /* 'H+' */ +#define HFSX_SIGNATURE 0x4858 /* 'HX' */ + +#define HFSP_VERSION 4 +#define HFSX_VERSION 5 + +#define HFS_HARD_LOCK 7 +#define HFS_UNMOUNTED 8 +#define HFS_BAD_SPARED 9 +#define HFS_SOFT_LOCK 15 +#define HFSP_NO_CACHE 10 +#define HFSP_INCONSISTENT 11 +#define HFSP_REUSE_CNID 12 +#define HFSP_JOURNALED 13 + +#define HFS_IDX_NODE 0x00 +#define HFS_HDR_NODE 0x01 +#define HFS_MAP_NODE 0x02 +#define HFS_LEAF_NODE 0xFF + +#define HFS_FIRST_REC 0x0E +#define HFS_NSD_HD_REC 0x78 +#define HFS_MAP_REC 0xF8 + +#define HFS_DATA_FORK 0x00 +#define HFS_RES_FORK 0xFF + +#define HFS_CAT_DIR 0x01 +#define HFS_CAT_FILE 0x02 +#define HFS_CAT_DIR_TH 0x03 +#define HFS_CAT_FILE_TH 0x04 + +#define HFSP_ATTR_INLINE 0x10 +#define HFSP_ATTR_FORK 0x20 +#define HFSP_ATTR_EXTENTS 0x30 + +#define HFS_ROOT_PAR_ID 0x01 +#define HFS_ROOT_DIR_ID 0x02 +#define HFS_XTENT_ID 0x03 +#define HFS_CATALOG_ID 0x04 +#define HFS_BAD_BLOCK_ID 0x05 +#define HFSP_ALLOC_ID 0x06 +#define HFSP_STARTUP_ID 0x07 +#define HFSP_ATTRIB_ID 0x08 +#define HFSP_BOGUS_ID 0x0F +#define HFSP_FIRST_AV_ID 0x10 + +#define HFSJ_JOURN_IN_FS 0x00 +#define HFSJ_JOURN_OTHER_DEV 0x01 +#define HFSJ_JOURN_NEED_INIT 0x02 + +#define HFSJ_HEADER_MAGIC 0x4a4e4c78 +#define HFSJ_ENDIAN_MAGIC 0x12345678 + +#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */ +#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */ + +#define HFS_EXT_NB 3 +#define HFSP_EXT_NB 8 + +/* Define the filenames used by the FS extractor */ +#ifdef HFS_EXTRACT_FS + +#define HFS_MDB_FILENAME "mdb.hfs" +#define HFS_CATALOG_FILENAME "catalog.hfs" +#define HFS_EXTENTS_FILENAME "extents.hfs" +#define HFS_BITMAP_FILENAME "bitmap.hfs" + +#define HFSP_VH_FILENAME "vh.hfsplus" +#define HFSP_CATALOG_FILENAME "catalog.hfsplus" +#define HFSP_EXTENTS_FILENAME "extents.hfsplus" +#define HFSP_BITMAP_FILENAME "bitmap.hfsplus" +#define HFSP_ATTRIB_FILENAME "attributes.hfsplus" +#define HFSP_STARTUP_FILENAME "startup.hfsplus" + +#endif /* HFS_EXTRACT_FS */ + + + +/* ----------------------------------- */ +/* -- HFS DATA STRUCTURES -- */ +/* ----------------------------------- */ + +/* Extent descriptor */ +struct __attribute__ ((packed)) _HfsExtDescriptor { + uint16_t start_block; + uint16_t block_count; +}; +typedef struct _HfsExtDescriptor HfsExtDescriptor; +typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB]; + +/* Volume header */ +struct __attribute__ ((packed)) _HfsMasterDirectoryBlock { + uint16_t signature; + uint32_t create_date; + uint32_t modify_date; + uint16_t volume_attributes; + uint16_t files_in_root; + uint16_t volume_bitmap_block; /* in sectors */ + uint16_t next_allocation; + uint16_t total_blocks; + uint32_t block_size; /* in bytes */ + uint32_t def_clump_size; /* in bytes */ + uint16_t start_block; /* in sectors */ + uint32_t next_free_node; + uint16_t free_blocks; + uint8_t name_length; + char name[27]; + uint32_t backup_date; + uint16_t backup_number; + uint32_t write_count; + uint32_t extents_clump; + uint32_t catalog_clump; + uint16_t dirs_in_root; + uint32_t file_count; + uint32_t dir_count; + uint32_t finder_info[8]; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint16_t volume_cache_size; /* in blocks */ + uint16_t bitmap_cache_size; /* in blocks */ + uint16_t common_cache_size; /* in blocks */ + } legacy; + struct __attribute__ ((packed)) { + uint16_t signature; + HfsExtDescriptor location; + } embedded; + } old_new; + uint32_t extents_file_size; /* in bytes, block size multiple */ + HfsExtDataRec extents_file_rec; + uint32_t catalog_file_size; /* in bytes, block size multiple */ + HfsExtDataRec catalog_file_rec; +}; +typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock; + +/* B*-Tree Node Descriptor */ +struct __attribute__ ((packed)) _HfsNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsNodeDescriptor HfsNodeDescriptor; + +/* Header record of a whole B*-Tree */ +struct __attribute__ ((packed)) _HfsHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; + int8_t reserved[76]; +}; +typedef struct _HfsHeaderRecord HfsHeaderRecord; + +/* Catalog key for B*-Tree lookup in the catalog file */ +struct __attribute__ ((packed)) _HfsCatalogKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t reserved; + uint32_t parent_ID; + uint8_t name_length; + char name[31]; /* in fact physicaly 1 upto 31 */ +}; +typedef struct _HfsCatalogKey HfsCatalogKey; + +/* Extents overflow key for B*-Tree lookup */ +struct __attribute__ ((packed)) _HfsExtentKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t type; /* data or ressource fork */ + uint32_t file_ID; + uint16_t start; +}; +typedef struct _HfsExtentKey HfsExtentKey; + +/* Catalog subdata case directory */ +struct __attribute__ ((packed)) _HfsDir { + uint16_t flags; + uint16_t valence; /* number of files in this directory */ + uint32_t dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t reserved[4]; +}; +typedef struct _HfsDir HfsDir; + +/* Catalog subdata case file */ +struct __attribute__ ((packed)) _HfsFile { + int8_t flags; + int8_t type; /* should be 0 */ + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + uint32_t file_ID; + uint16_t data_start_block; + uint32_t data_sz_byte; + uint32_t data_sz_block; + uint16_t res_start_block; + uint32_t res_sz_byte; + uint32_t res_sz_block; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint16_t clump_size; + HfsExtDataRec extents_data; + HfsExtDataRec extents_res; + uint32_t reserved; +}; +typedef struct _HfsFile HfsFile; + +/* Catalog subdata case directory thread */ +struct __attribute__ ((packed)) _HfsDirTh { + uint32_t reserved[2]; + uint32_t parent_ID; + int8_t name_length; + char name[31]; +}; +typedef struct _HfsDirTh HfsDirTh; + +/* Catalog subdata case file thread */ +typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */ + +/* Catalog data */ +struct __attribute__ ((packed)) _HfsCatalog { + int8_t type; + int8_t reserved; + union { + HfsDir dir; + HfsFile file; + HfsDirTh dir_th; + HfsFileTh file_th; + } sel; +}; +typedef struct _HfsCatalog HfsCatalog; + + + +/* ------------------------------------ */ +/* -- HFS+ DATA STRUCTURES -- */ +/* ------------------------------------ */ + +/* documented since 2004 in tn1150 */ +struct __attribute__ ((packed)) _HfsPPerms { + uint32_t owner_ID; + uint32_t group_ID; + uint32_t permissions; + uint32_t special_devices; +}; +typedef struct _HfsPPerms HfsPPerms; + +/* HFS+ extent descriptor*/ +struct __attribute__ ((packed)) _HfsPExtDescriptor { + uint32_t start_block; + uint32_t block_count; +}; +typedef struct _HfsPExtDescriptor HfsPExtDescriptor; +typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB]; + +/* HFS+ fork data structure */ +struct __attribute__ ((packed)) _HfsPForkData { + uint64_t logical_size; + uint32_t clump_size; + uint32_t total_blocks; + HfsPExtDataRec extents; +}; +typedef struct _HfsPForkData HfsPForkData; + +/* HFS+ catalog node ID */ +typedef uint32_t HfsPNodeID; + +/* HFS+ file names */ +typedef uint16_t unichar; +struct __attribute__ ((packed)) _HfsPUniStr255 { + uint16_t length; + unichar unicode[255]; /* 1 upto 255 */ +}; +typedef struct _HfsPUniStr255 HfsPUniStr255; + +/* HFS+ volume header */ +struct __attribute__ ((packed)) _HfsPVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t last_mounted_version; + uint32_t journal_info_block; + + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + + uint32_t file_count; + uint32_t dir_count; + + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + + uint32_t next_allocation; + uint32_t res_clump_size; + uint32_t data_clump_size; + HfsPNodeID next_catalog_ID; + + uint32_t write_count; + uint64_t encodings_bitmap; + + uint8_t finder_info[32]; + + HfsPForkData allocation_file; + HfsPForkData extents_file; + HfsPForkData catalog_file; + HfsPForkData attributes_file; + HfsPForkData startup_file; +}; +typedef struct _HfsPVolumeHeader HfsPVolumeHeader; + +/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */ +struct __attribute__ ((packed)) _HfsPNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor; + +/* Header record of a whole HFS+ B-Tree. */ +struct __attribute__ ((packed)) _HfsPHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; /* same as hfs btree until here */ + uint16_t reserved1; + + uint32_t clump_size; + uint8_t btree_type; /* must be 0 for HFS+ B-Tree */ + uint8_t key_compare_type; /* hfsx => 0xCF = case folding */ + /* 0xBC = binary compare */ + /* otherwise, reserved */ + uint32_t attributes; + uint32_t reserved3[16]; +}; +typedef struct _HfsPHeaderRecord HfsPHeaderRecord; + +/* Catalog key for B-Tree lookup in the HFS+ catalog file */ +struct __attribute__ ((packed)) _HfsPCatalogKey { + uint16_t key_length; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPCatalogKey HfsPCatalogKey; + +/* HFS+ catalog subdata case dir */ +struct __attribute__ ((packed)) _HfsPDir { + uint16_t flags; + uint32_t valence; + HfsPNodeID dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved; +}; +typedef struct _HfsPDir HfsPDir; + +/* HFS+ catalog subdata case file */ +struct __attribute__ ((packed)) _HfsPFile { + uint16_t flags; + uint32_t reserved1; + HfsPNodeID file_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved2; + + HfsPForkData data_fork; + HfsPForkData res_fork; +}; +typedef struct _HfsPFile HfsPFile; + +/* HFS+ catalog subdata case thread */ +struct __attribute__ ((packed)) _HfsPThread { + int16_t reserved; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPThread HfsPDirTh; +typedef struct _HfsPThread HfsPFileTh; + +/* HFS+ Catalog leaf data */ +struct __attribute__ ((packed)) _HfsPCatalog { + int16_t type; + union { + HfsPDir dir; + HfsPFile file; + HfsPDirTh dir_th; + HfsPFileTh file_th; + } sel; +}; +typedef struct _HfsPCatalog HfsPCatalog; + +/* HFS+ extents file key */ +struct __attribute__ ((packed)) _HfsPExtentKey { + uint16_t key_length; + uint8_t type; + uint8_t pad; + HfsPNodeID file_ID; + uint32_t start; +}; +typedef struct _HfsPExtentKey HfsPExtentKey; + +/* extent file data is HfsPExtDataRec */ + +/* Fork data attribute file */ +struct __attribute__ ((packed)) _HfsPForkDataAttr { + uint32_t record_type; + uint32_t reserved; + union __attribute__ ((packed)) { + HfsPForkData fork; + HfsPExtDataRec extents; + } fork_res; +}; +typedef struct _HfsPForkDataAttr HfsPForkDataAttr; + + +/* ----------- Journal data structures ----------- */ + +/* Info block : stored in a block # defined in the VH */ +struct __attribute__ ((packed)) _HfsJJournalInfoBlock { + uint32_t flags; + uint32_t device_signature[8]; + uint64_t offset; + uint64_t size; + uint32_t reserved[32]; +}; +typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock; + +struct __attribute__ ((packed)) _HfsJJournalHeader { + uint32_t magic; + uint32_t endian; + uint64_t start; + uint64_t end; + uint64_t size; + uint32_t blhdr_size; + uint32_t checksum; + uint32_t jhdr_size; +}; +typedef struct _HfsJJournalHeader HfsJJournalHeader; + +struct __attribute__ ((packed)) _HfsJBlockInfo { + uint64_t bnum; /* sector number */ + uint32_t bsize; /* size in bytes */ + uint32_t next; +}; +typedef struct _HfsJBlockInfo HfsJBlockInfo; + +struct __attribute__ ((packed)) _HfsJBlockListHeader { + uint16_t max_blocks; /* reserved */ + uint16_t num_blocks; + uint32_t bytes_used; + uint32_t checksum; + uint32_t pad; + HfsJBlockInfo binfo[1]; +}; +typedef struct _HfsJBlockListHeader HfsJBlockListHeader; + + + +/* ---------------------------------------- */ +/* -- INTERNAL DATA STRUCTURES -- */ +/* ---------------------------------------- */ + +/* Data of an opened HFS file */ +struct _HfsPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + uint32_t CNID; /* disk order (BE) */ + HfsExtDataRec first; /* disk order (BE) */ + HfsExtDataRec cache; /* disk order (BE) */ + uint16_t start_cache; /* CPU order */ +}; +typedef struct _HfsPrivateFile HfsPrivateFile; + +/* To store bad block list */ +struct _HfsPrivateLinkExtent { + HfsExtDescriptor extent; + struct _HfsPrivateLinkExtent* next; +}; +typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent; + +/* HFS Filesystem specific data */ +struct _HfsPrivateFSData { + uint8_t alloc_map[(1<<16) / 8]; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFile* extent_file; + HfsPrivateFile* catalog_file; + HfsPrivateLinkExtent* bad_blocks_xtent_list; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; +}; +typedef struct _HfsPrivateFSData HfsPrivateFSData; + +/* Generic btree key */ +struct __attribute__ ((packed)) _HfsPrivateGenericKey { + uint8_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey; + +/* ----- HFS+ ----- */ + +/* Data of an opened HFS file */ +struct _HfsPPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + HfsPNodeID CNID; /* disk order (BE) */ + HfsPExtDataRec first; /* disk order (BE) */ + HfsPExtDataRec cache; /* disk order (BE) */ + uint32_t start_cache; /* CPU order */ +}; +typedef struct _HfsPPrivateFile HfsPPrivateFile; + +struct _HfsPPrivateExtent { + PedSector start_sector; + PedSector sector_count; +}; +typedef struct _HfsPPrivateExtent HfsPPrivateExtent; + +/* To store bad block list */ +struct _HfsPPrivateLinkExtent { + HfsPExtDescriptor extent; + struct _HfsPPrivateLinkExtent* next; +}; +typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent; + +/* HFS+ file system specific data */ +struct _HfsPPrivateFSData { + PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */ + PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */ + uint8_t* alloc_map; + uint8_t* dirty_alloc_map; + HfsPVolumeHeader* vh; + HfsPPrivateFile* extents_file; + HfsPPrivateFile* catalog_file; + HfsPPrivateFile* attributes_file; + HfsPPrivateFile* allocation_file; + HfsPPrivateLinkExtent* bad_blocks_xtent_list; + uint32_t jib_start_block; + uint32_t jl_start_block; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; + char free_geom; /* 1 = plus_geom must be freed */ +}; +typedef struct _HfsPPrivateFSData HfsPPrivateFSData; + +/* Generic + btree key */ +struct __attribute__ ((packed)) _HfsPPrivateGenericKey { + uint16_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey; + +/* ---- common ---- */ + +/* node and lead record reference for a BTree search */ +struct _HfsCPrivateLeafRec { + unsigned int node_size; /* in sectors */ + unsigned int node_number; + unsigned int record_pos; + unsigned int record_number; +}; +typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec; + +extern uint8_t* hfs_block; +extern uint8_t* hfsp_block; +extern unsigned hfs_block_count; +extern unsigned hfsp_block_count; + +#endif /* _HFS_H */ diff --git a/libparted/fs/hfs/journal.c b/libparted/fs/hfs/journal.c new file mode 100644 index 0000000..6e5c267 --- /dev/null +++ b/libparted/fs/hfs/journal.c @@ -0,0 +1,389 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "reloc_plus.h" + +#include "journal.h" + +static int hfsj_vh_replayed = 0; +static int is_le = 0; + +static uint32_t +hfsj_calc_checksum(uint8_t *ptr, int len) +{ + int i; + uint32_t cksum=0; + + for (i=0; i < len; i++, ptr++) { + cksum = (cksum << 8) ^ (cksum + *ptr); + } + + return (~cksum); +} + +int +hfsj_update_jib(PedFileSystem* fs, uint32_t block) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block); + + if (!hfsplus_update_vh (fs)) + return 0; + + priv_data->jib_start_block = block; + return 1; +} + +int +hfsj_update_jl(PedFileSystem* fs, uint32_t block) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector sector; + uint64_t offset; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJJournalInfoBlock* jib; + int binsect; + + binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / PED_SECTOR_SIZE_DEFAULT; + sector = (PedSector) priv_data->jib_start_block * binsect; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jib = (HfsJJournalInfoBlock*) buf; + + offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect; + jib->offset = HFS_CPU_TO_64(offset, is_le); + + if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) + || !ped_geometry_sync(priv_data->plus_geom)) + return 0; + + priv_data->jl_start_block = block; + return 1; +} + +/* Return the sector in the journal that is after the area read */ +/* or 0 on error */ +static PedSector +hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh, + PedSector journ_sect, PedSector journ_length, + PedSector read_sect, unsigned int nb_sect, + void* buf) +{ + int r; + + while (nb_sect--) { + r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1); + if (!r) return 0; + + buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT; + read_sect++; + if (read_sect == journ_length) + read_sect = 1; /* skip journal header + which is asserted to be + 1 sector long */ + } + + return read_sect; +} + +static int +hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh, + PedSector jsector, PedSector jlength) +{ + PedSector start, sector; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJBlockListHeader* blhdr; + uint8_t* block; + unsigned int blhdr_nbsect; + int i, r; + uint32_t cksum, size; + + blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / PED_SECTOR_SIZE_DEFAULT; + blhdr = (HfsJBlockListHeader*) + ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT); + if (!blhdr) return 0; + + start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT; + do { + start = hfsj_journal_read(priv_data->plus_geom, jh, jsector, + jlength, start, blhdr_nbsect, blhdr); + if (!start) goto err_replay; + + cksum = HFS_32_TO_CPU(blhdr->checksum, is_le); + blhdr->checksum = 0; + if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){ + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad block list header checksum.")); + goto err_replay; + } + blhdr->checksum = HFS_CPU_TO_32(cksum, is_le); + + for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) { + size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le); + sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le); + if (!size) continue; + if (size % PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid size of a transaction " + "block while replaying the journal " + "(%i bytes)."), + size); + goto err_replay; + } + block = (uint8_t*) ped_malloc(size); + if (!block) goto err_replay; + start = hfsj_journal_read(priv_data->plus_geom, jh, + jsector, jlength, start, + size / PED_SECTOR_SIZE_DEFAULT, + block); + if (!start) { + free (block); + goto err_replay; + } + /* the sector stored in the journal seems to be + relative to the begin of the block device which + contains the hfs+ journaled volume */ + if (sector != ~0LL) + r = ped_geometry_write (fs->geom, block, sector, + size / PED_SECTOR_SIZE_DEFAULT); + else + r = 1; + free (block); + /* check if wrapper mdb or vh with no wrapper has + changed */ + if ( (sector != ~0LL) + && (2 >= sector) + && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) ) + hfsj_vh_replayed = 1; + /* check if vh of embedded hfs+ has changed */ + if ( (sector != ~0LL) + && (priv_data->plus_geom != fs->geom) + && (sector + + fs->geom->start + - priv_data->plus_geom->start <= 2) + && (sector + + size / PED_SECTOR_SIZE_DEFAULT + + fs->geom->start + - priv_data->plus_geom->start > 2) ) + hfsj_vh_replayed = 1; + if (!r) goto err_replay; + } + } while (blhdr->binfo[0].next); + + jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le); + + free (blhdr); + return (ped_geometry_sync (fs->geom)); + +err_replay: + free (blhdr); + return 0; +} + +/* 0 => Failure, don't continue to open ! */ +/* 1 => Success, the journal has been completly replayed, or don't need to */ +int +hfsj_replay_journal(PedFileSystem* fs) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector sector, length; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJJournalInfoBlock* jib; + HfsJJournalHeader* jh; + int binsect; + uint32_t cksum; + + binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT; + priv_data->jib_start_block = + PED_BE32_TO_CPU(priv_data->vh->journal_info_block); + sector = (PedSector) priv_data->jib_start_block * binsect; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jib = (HfsJJournalInfoBlock*) buf; + + if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) + && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { + priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le) + / ( PED_SECTOR_SIZE_DEFAULT * binsect ); + } + + if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT)) + return 1; + + if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) + || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Journal stored outside of the volume are " + "not supported. Try to desactivate the " + "journal and run Parted again.")); + return 0; + } + + if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT) + || (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Journal offset or size is not multiple of " + "the sector size.")); + return 0; + } + + sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT; + length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT; + + jib = NULL; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jh = (HfsJJournalHeader*) buf; + + if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC)) + is_le = 1; + + if ( (jh->magic != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le)) + || (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Incorrect magic values in the journal header.")); + return 0; + } + + if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT) + || (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT + != (uint64_t)length) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Journal size mismatch between journal info block " + "and journal header.")); + return 0; + } + + if ( (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT) + || (HFS_64_TO_CPU(jh->end, is_le) % PED_SECTOR_SIZE_DEFAULT) + || (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) + || (HFS_32_TO_CPU(jh->jhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Some header fields are not multiple of the sector " + "size.")); + return 0; + } + + if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The sector size stored in the journal is not 512 " + "bytes. Parted only supports 512 bytes length " + "sectors.")); + return 0; + } + + cksum = HFS_32_TO_CPU(jh->checksum, is_le); + jh->checksum = 0; + if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad journal checksum.")); + return 0; + } + jh->checksum = HFS_CPU_TO_32(cksum, is_le); + + /* The 2 following test are in the XNU Darwin source code */ + /* so I assume they're needed */ + if (jh->start == jh->size) + jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le); + if (jh->end == jh->size) + jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le); + + if (jh->start == jh->end) + return 1; + + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("The journal is not empty. Parted must replay the " + "transactions before opening the file system. This will " + "modify the file system.")) + != PED_EXCEPTION_FIX) + return 0; + + while (jh->start != jh->end) { + /* Replay one complete transaction */ + if (!hfsj_replay_transaction(fs, jh, sector, length)) + return 0; + + /* Recalculate cksum of the journal header */ + jh->checksum = 0; /* need to be 0 while calculating the cksum */ + cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh)); + jh->checksum = HFS_CPU_TO_32(cksum, is_le); + + /* Update the Journal Header */ + if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) + || !ped_geometry_sync(priv_data->plus_geom)) + return 0; + } + + if (hfsj_vh_replayed) { + /* probe could have reported incorrect info ! */ + /* is there a way to ask parted to quit ? */ + ped_exception_throw( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("The volume header or the master directory block has " + "changed while replaying the journal. You should " + "restart Parted.")); + return 0; + } + + return 1; +} + +#endif /* DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/journal.h b/libparted/fs/hfs/journal.h new file mode 100644 index 0000000..4a40e02 --- /dev/null +++ b/libparted/fs/hfs/journal.h @@ -0,0 +1,44 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _JOURNAL_H +#define _JOURNAL_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsj_replay_journal(PedFileSystem* fs); + +int +hfsj_update_jib(PedFileSystem* fs, uint32_t block); + +int +hfsj_update_jl(PedFileSystem* fs, uint32_t block); + +#define HFS_16_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_LE16_TO_CPU(x) : (uint16_t)PED_BE16_TO_CPU(x)) +#define HFS_32_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_LE32_TO_CPU(x) : (uint32_t)PED_BE32_TO_CPU(x)) +#define HFS_64_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_LE64_TO_CPU(x) : (uint64_t)PED_BE64_TO_CPU(x)) +#define HFS_CPU_TO_16(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_CPU_TO_LE16(x) : (uint16_t)PED_CPU_TO_BE16(x)) +#define HFS_CPU_TO_32(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_CPU_TO_LE32(x) : (uint32_t)PED_CPU_TO_BE32(x)) +#define HFS_CPU_TO_64(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_CPU_TO_LE64(x) : (uint64_t)PED_CPU_TO_BE64(x)) + +#endif /* _JOURNAL_H */ diff --git a/libparted/fs/hfs/probe.c b/libparted/fs/hfs/probe.c new file mode 100644 index 0000000..8c656cf --- /dev/null +++ b/libparted/fs/hfs/probe.c @@ -0,0 +1,230 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "probe.h" + +int +hfsc_can_use_geom (PedGeometry* geom) +{ + PedDevice* dev; + + dev = geom->dev; + PED_ASSERT (geom != NULL); + PED_ASSERT (dev != NULL); + + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't use HFS file systems on disks " + "with a sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + return 1; +} + +/* Probe an HFS volume, detecting it even if +it is in fact a wrapper to an HFS+ volume */ +/* Used by hfsplus_probe and hfs_probe */ +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + HfsMasterDirectoryBlock *mdb; + PedGeometry* geom_ret; + PedSector search, max; + + PED_ASSERT (geom != NULL); + PED_ASSERT (hfsc_can_use_geom (geom)); + + mdb = (HfsMasterDirectoryBlock *) buf; + + /* is 5 an intelligent value ? */ + if ((geom->length < 5) + || (!ped_geometry_read (geom, buf, 2, 1)) + || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) ) + return NULL; + + search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block) + + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks) + * (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT ))); + max = search + (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT); + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} + +PedGeometry* +hfsplus_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_ret = hfs_and_wrapper_probe(geom))) { + /* HFS+ is embedded in an HFS volume ? */ + HfsMasterDirectoryBlock *mdb; + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1) + || (mdb->old_new.embedded.signature + != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } else { + /* This is a standalone HFS+ volume ? */ + PedSector search, max; + HfsPVolumeHeader *vh; + vh = (HfsPVolumeHeader *) buf; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) + return NULL; + + /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */ + /* But previous versions of my implementation used to */ + /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */ + /* (blocks-1)*sz-1 has to be scanned last, because */ + /* it can belong to a regular file */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) + 2; + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE)) + return geom_ret; + } + search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 1; + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1) + || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } +} + +PedGeometry* +hfs_probe (PedGeometry* geom) +{ + PedGeometry* geom_base; + PedGeometry* geom_plus = NULL; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_base = hfs_and_wrapper_probe(geom)) + && (!(geom_plus = hfsplus_probe(geom_base)))) + return geom_base; + else { + if (geom_base) ped_geometry_destroy (geom_base); + if (geom_plus) ped_geometry_destroy (geom_plus); + return NULL; + } +} + +PedGeometry* +hfsx_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector search, max; + HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE))) + return NULL; + + /* unlike the hfs+ code, which should be kept compatible + with my old previous implementations, we only care here + about legal alternate VH positions, like TN1150 describes them */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ); + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} diff --git a/libparted/fs/hfs/probe.h b/libparted/fs/hfs/probe.h new file mode 100644 index 0000000..48d14b2 --- /dev/null +++ b/libparted/fs/hfs/probe.h @@ -0,0 +1,43 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _PROBE_H +#define _PROBE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsc_can_use_geom (PedGeometry* geom); + +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom); + +PedGeometry* +hfsplus_probe (PedGeometry* geom); + +PedGeometry* +hfs_probe (PedGeometry* geom); + +PedGeometry* +hfsx_probe (PedGeometry* geom); + +#endif /* _PROBE_H */ diff --git a/libparted/fs/hfs/reloc.c b/libparted/fs/hfs/reloc.c new file mode 100644 index 0000000..81d1057 --- /dev/null +++ b/libparted/fs/hfs/reloc.c @@ -0,0 +1,669 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file.h" +#include "advfs.h" +#include "cache.h" + +#include "reloc.h" + +/* This function moves data of size blocks starting + at block *ptr_fblock to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +static int +hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int i, ok = 0; + unsigned int next_to_fblock; + unsigned int start, stop; + + PED_ASSERT (hfs_block != NULL); + PED_ASSERT (*ptr_to_fblock <= *ptr_fblock); + /* quiet gcc */ + start = stop = 0; + +/* + Try to fit the extent AT or _BEFORE_ the wanted place, + or then in the gap between dest and source. + If failed try to fit the extent after source, for 2 pass relocation + The extent is always copied in a non overlapping way +*/ + + /* Backward search */ + /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ + if (*ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_fblock < *ptr_to_fblock+size ? + *ptr_fblock : *ptr_to_fblock+size; + while (start && stop-start != size) { + --start; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) + stop = start; + } + ok = (stop-start == size); + } + + /* Forward search */ + /* 1 pass relocation in the gap merged with 2 pass reloc after source */ + if (!ok && *ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_to_fblock+1; + while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks) + && stop-start != size) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + } + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + unsigned int j; + unsigned int start_block = + PED_BE16_TO_CPU (priv_data->mdb->start_block ); + unsigned int block_sz = + (PED_BE32_TO_CPU (priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT); + + if (stop > *ptr_to_fblock && stop <= *ptr_fblock) + /* Fit in the gap */ + next_to_fblock = stop; + else + /* Before or after the gap */ + next_to_fblock = *ptr_to_fblock; + + /* move blocks */ + for (i = 0; i < size; /*i+=j*/) { + PedSector abs_sector; + unsigned int ai; + + j = size - i; j = (j < hfs_block_count) ? + j : hfs_block_count ; + + abs_sector = start_block + + (PedSector) (*ptr_fblock + i) * block_sz; + if (!ped_geometry_read (fs->geom, hfs_block, abs_sector, + block_sz * j)) + return -1; + + abs_sector = start_block + + (PedSector) (start + i) * block_sz; + if (!ped_geometry_write (fs->geom,hfs_block,abs_sector, + block_sz * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + /* free source block */ + CLR_BLOC_OCCUPATION(priv_data->alloc_map, + *ptr_fblock + i); + + /* set dest block */ + SET_BLOC_OCCUPATION(priv_data->alloc_map, + start + i); + } + } + if (!ped_geometry_sync_fast (fs->geom)) + return -1; + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + if (*ptr_fblock != *ptr_to_fblock) + /* not enough room, but try to continue */ + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("An extent has not been relocated.")); + start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = start + size; + } + + return start; +} + +/* Update MDB */ +/* Return 0 if an error occurred */ +/* Return 1 if everything ok */ +int +hfs_update_mdb (PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + + if (!ped_geometry_read (fs->geom, node, 2, 1)) + return 0; + memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock)); + if ( !ped_geometry_write (fs->geom, node, 2, 1) + || !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1) + || !ped_geometry_sync_fast (fs->geom)) + return 0; + return 1; +} + +/* Generic relocator */ +/* replace previous hfs_do_move_* */ +static int +hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src, + unsigned int *ptr_dest, HfsCPrivateCache* cache, + HfsCPrivateExtent* ref) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateFile* file; + HfsExtDescriptor* extent; + HfsCPrivateExtent* move; + int new_start; + + new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest, + ref->ext_length); + if (new_start == -1) return -1; + + if (ref->ext_start != (unsigned) new_start) { + /* Load, modify & save */ + switch (ref->where) { + /******** MDB *********/ + case CR_PRIM_CAT : + priv_data->catalog_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + goto CR_PRIM; + case CR_PRIM_EXT : + priv_data->extent_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + CR_PRIM : + extent = ( HfsExtDescriptor* ) + ( (uint8_t*)priv_data->mdb + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + if (!hfs_update_mdb(fs)) return -1; + break; + + /********* BTREE *******/ + case CR_BTREE_EXT_CAT : + if (priv_data->catalog_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE16(ref->ext_start)) + priv_data->catalog_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + case CR_BTREE_EXT_0 : + file = priv_data->extent_file; + goto CR_BTREE; + case CR_BTREE_CAT : + file = priv_data->catalog_file; + CR_BTREE: + PED_ASSERT(ref->sect_by_block == 1 + && ref->ref_offset < PED_SECTOR_SIZE_DEFAULT); + if (!hfs_file_read_sector(file, node, ref->ref_block)) + return -1; + extent = ( HfsExtDescriptor* ) (node + ref->ref_offset); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + if (!hfs_file_write_sector(file, node, ref->ref_block) + || !ped_geometry_sync_fast (fs->geom)) + return -1; + break; + + /********** BUG ********/ + default : + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("A reference to an extent comes from a place " + "it should not. You should check the file " + "system!")); + return -1; + break; + } + + /* Update the cache */ + move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); + if (!move) return -1; /* "cleanly" fail */ + PED_ASSERT(move == ref); /* generate a bug */ + } + + return new_start; +} + +/* 0 error, 1 ok */ +static int +hfs_save_allocation(PedFileSystem* fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int map_sectors; + + map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + return ( ped_geometry_write (fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + map_sectors) ); +} + +/* This function moves an extent starting at block fblock to block to_fblock + if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occurred */ +/* Return 0 if no extent was found */ +/* Generic search thanks to the file system cache */ +static int +hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, + HfsCPrivateCache* cache) +{ + HfsCPrivateExtent* ref; + unsigned int old_start, new_start; + + /* Reference search powered by the cache... */ + /* This is the optimisation secret :) */ + ref = hfsc_cache_search_extent(cache, *ptr_fblock); + if (!ref) return 0; /* not found */ + + old_start = *ptr_fblock; + new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); + if (new_start == (unsigned int) -1) return -1; + if (new_start > old_start) { /* detect 2 pass reloc */ + new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref); + if (new_start == (unsigned int) -1 || new_start > old_start) + return -1; + } + + /* allocation bitmap save is not atomic with data relocation */ + /* so we only do it a few times, and without syncing */ + /* The unmounted bit protect us anyway */ + hfs_save_allocation(fs); + return 1; +} + +static int +hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsExtDescriptor* extent; + unsigned int j; + + extent = priv_data->mdb->extents_file_rec; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + 0, /* unused for mdb */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb), + 1, /* load/save only 1 sector */ + CR_PRIM_EXT, + j ) + ) + return 0; + } + + extent = priv_data->mdb->catalog_file_rec; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + 0, + ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb), + 1, + CR_PRIM_CAT, + j ) + ) + return 0; + } + + return 1; +} + +static int +hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsCatalogKey* catalog_key; + HfsCatalog* catalog_data; + HfsExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j; + + if (!priv_data->catalog_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS volume has no catalog file. " + "This is very unusual!")); + return 1; + } + + if (!hfs_file_read_sector (priv_data->catalog_file, node, 0)) + return 0; + header = (HfsHeaderRecord*)(node + PED_BE16_TO_CPU(*((uint16_t*) + (node+(PED_SECTOR_SIZE_DEFAULT-2))))); + + for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + leaf_node; + leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->catalog_file, + node, leaf_node)) + return 0; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; ++i) { + /* undocumented alignement */ + unsigned int skip; + catalog_key = (HfsCatalogKey*) (node + PED_BE16_TO_CPU( + *((uint16_t*)(node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); + skip = (1 + catalog_key->key_length + 1) & ~1; + catalog_data = (HfsCatalog*)( ((uint8_t*)catalog_key) + + skip ); + /* check for obvious error in FS */ + if (((uint8_t*)catalog_key - node < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + + if (catalog_data->type != HFS_CAT_FILE) continue; + + extent = catalog_data->sel.file.extents_data; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + CR_BTREE_CAT, + j ) + ) + return 0; + } + + extent = catalog_data->sel.file.extents_res; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + CR_BTREE_CAT, + j ) + ) + return 0; + } + } + } + + return 1; +} + +static int +hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsExtentKey* extent_key; + HfsExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j; + + if (!priv_data->extent_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS volume has no extents overflow " + "file. This is quite unusual!")); + return 1; + } + + if (!hfs_file_read_sector (priv_data->extent_file, node, 0)) + return 0; + header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT-2)))))); + + for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + leaf_node; + leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->extent_file, node, + leaf_node)) + return 0; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + uint8_t where; + extent_key = (HfsExtentKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(PED_SECTOR_SIZE_DEFAULT - 2*i))))); + /* size is cst */ + extent = (HfsExtDescriptor*)(((uint8_t*)extent_key) + + sizeof (HfsExtentKey)); + /* check for obvious error in FS */ + if (((uint8_t*)extent_key - node < HFS_FIRST_REC) + || ((uint8_t*)extent - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + + switch (extent_key->file_ID) { + case PED_CPU_TO_BE32 (HFS_XTENT_ID) : + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The extents overflow file should not" + " contain its own extents! You " + "should check the file system.")) + != PED_EXCEPTION_IGNORE) + return 0; + where = CR_BTREE_EXT_EXT; + break; + case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : + where = CR_BTREE_EXT_CAT; + break; + default : + where = CR_BTREE_EXT_0; + break; + } + + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + where, + j ) + ) + return 0; + } + } + } + + return 1; +} + +/* This function cache every extents start and length stored in any + fs structure into the adt defined in cache.[ch] + Returns NULL on failure */ +static HfsCPrivateCache* +hfs_cache_extents(PedFileSystem *fs, PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsCPrivateCache* ret; + unsigned int file_number, block_number; + + file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count); + block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks); + ret = hfsc_new_cache(block_number, file_number); + if (!ret) return NULL; + + if (!hfs_cache_from_mdb(ret, fs, timer) || + !hfs_cache_from_catalog(ret, fs, timer) || + !hfs_cache_from_extent(ret, fs, timer)) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not cache the file system in memory.")); + hfsc_delete_cache(ret); + return NULL; + } + + return ret; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + PedSector bytes_buff; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + HfsCPrivateCache* cache; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int divisor = PED_BE16_TO_CPU (mdb->total_blocks) + + 1 - start - to_free; + int ret; + + PED_ASSERT (!hfs_block); + + cache = hfs_cache_extents (fs, timer); + if (!cache) + return 0; + + /* Calculate the size of the copy buffer : + * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF + * takes the maximum number of HFS blocks so that the buffer + * will remain smaller than or equal to BYTES_MAX_BUFF, with + * a minimum of 1 HFS block */ + bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size) + * (PedSector) BLOCK_MAX_BUFF; + if (bytes_buff > BYTES_MAX_BUFF) { + hfs_block_count = BYTES_MAX_BUFF + / PED_BE32_TO_CPU (priv_data->mdb->block_size); + if (!hfs_block_count) + hfs_block_count = 1; + bytes_buff = (PedSector) hfs_block_count + * PED_BE32_TO_CPU (priv_data->mdb->block_size); + } else + hfs_block_count = BLOCK_MAX_BUFF; + + /* If the cache code requests more space, give it to him */ + if (bytes_buff < hfsc_cache_needed_buffer (cache)) + bytes_buff = hfsc_cache_needed_buffer (cache); + + hfs_block = (uint8_t*) ped_malloc (bytes_buff); + if (!hfs_block) + goto error_cache; + + if (!hfs_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks list could not be loaded.")); + goto error_alloc; + } + + while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock) + && (!hfs_is_bad_block (fs, fblock))) { + if (!(ret = hfs_move_extent_starting_at (fs, &fblock, + &to_fblock, cache))) + to_fblock = ++fblock; + else if (ret == -1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred during extent " + "relocation.")); + goto error_alloc; + } + } else { + fblock++; + } + + ped_timer_update(timer, (float)(to_fblock - start)/divisor); + } + + free (hfs_block); hfs_block = NULL; hfs_block_count = 0; + hfsc_delete_cache (cache); + return 1; + +error_alloc: + free (hfs_block); hfs_block = NULL; hfs_block_count = 0; +error_cache: + hfsc_delete_cache (cache); + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/reloc.h b/libparted/fs/hfs/reloc.h new file mode 100644 index 0000000..536c071 --- /dev/null +++ b/libparted/fs/hfs/reloc.h @@ -0,0 +1,35 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _RELOC_H +#define _RELOC_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfs_update_mdb (PedFileSystem *fs); + +int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free); + +#endif /* _RELOC_H */ diff --git a/libparted/fs/hfs/reloc_plus.c b/libparted/fs/hfs/reloc_plus.c new file mode 100644 index 0000000..750927f --- /dev/null +++ b/libparted/fs/hfs/reloc_plus.c @@ -0,0 +1,942 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file_plus.h" +#include "advfs_plus.h" +#include "cache.h" +#include "journal.h" + +#include "reloc_plus.h" + +/* This function moves data of size blocks starting at block *ptr_fblock + to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +/* -1 is ok because there can only be 2^32-1 blocks, so the max possible + last one is 2^32-2 (and anyway it contains Alternate VH), so + -1 (== 2^32-1[2^32]) never represent a valid block */ +static int +hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int i, ok = 0; + unsigned int next_to_fblock; + unsigned int start, stop; + + PED_ASSERT (hfsp_block != NULL); + PED_ASSERT (*ptr_to_fblock <= *ptr_fblock); + /* quiet GCC */ + start = stop = 0; + +/* + Try to fit the extent AT or _BEFORE_ the wanted place, + or then in the gap between dest and source. + If failed try to fit the extent after source, for 2 pass relocation + The extent is always copied in a non overlapping way +*/ + + /* Backward search */ + /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ + if (*ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_fblock < *ptr_to_fblock+size ? + *ptr_fblock : *ptr_to_fblock+size; + while (start && stop-start != size) { + --start; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) + stop = start; + } + ok = (stop-start == size); + } + + /* Forward search */ + /* 1 pass relocation in the gap merged with 2 pass reloc after source */ + if (!ok && *ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_to_fblock+1; + while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks) + && stop-start != size) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + } + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + PedSector abs_sector; + unsigned int ai, j, block; + unsigned int block_sz = (PED_BE32_TO_CPU ( + priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT); + + if (stop > *ptr_to_fblock && stop <= *ptr_fblock) + /* Fit in the gap */ + next_to_fblock = stop; + else + /* Before or after the gap */ + next_to_fblock = *ptr_to_fblock; + + /* move blocks */ + for (i = 0; i < size; /*i++*/) { + j = size - i; j = (j < hfsp_block_count) ? + j : hfsp_block_count ; + + abs_sector = (PedSector) (*ptr_fblock + i) * block_sz; + if (!ped_geometry_read (priv_data->plus_geom, + hfsp_block, abs_sector, + block_sz * j)) + return -1; + + abs_sector = (PedSector) (start + i) * block_sz; + if (!ped_geometry_write (priv_data->plus_geom, + hfsp_block, abs_sector, + block_sz * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + /* free source block */ + block = *ptr_fblock + i; + CLR_BLOC_OCCUPATION(priv_data->alloc_map,block); + SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, + block/(PED_SECTOR_SIZE_DEFAULT*8)); + + /* set dest block */ + block = start + i; + SET_BLOC_OCCUPATION(priv_data->alloc_map,block); + SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, + block/(PED_SECTOR_SIZE_DEFAULT*8)); + } + } + if (!ped_geometry_sync_fast (priv_data->plus_geom)) + return -1; + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + if (*ptr_fblock != *ptr_to_fblock) + /* not enough room */ + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("An extent has not been relocated.")); + start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = start + size; + } + + return start; +} + +/* Returns 0 on error */ +/* 1 on succes */ +int +hfsplus_update_vh (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + + if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1)) + return 0; + memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader)); + if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1) + || !ped_geometry_write (priv_data->plus_geom, node, + priv_data->plus_geom->length - 2, 1) + || !ped_geometry_sync_fast (priv_data->plus_geom)) + return 0; + return 1; +} + +static int +hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src, + unsigned int *ptr_dest, HfsCPrivateCache* cache, + HfsCPrivateExtent* ref) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateFile* file; + HfsPExtDescriptor* extent; + HfsCPrivateExtent* move; + int new_start; + + new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest, + ref->ext_length); + + if (new_start == -1) return -1; + + if (ref->ext_start != (unsigned) new_start) { + switch (ref->where) { + /************ VH ************/ + case CR_PRIM_CAT : + priv_data->catalog_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_EXT : + priv_data->extents_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_ATTR : + priv_data->attributes_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_ALLOC : + priv_data->allocation_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_START : + /* No startup file opened */ + CR_PRIM : + extent = ( HfsPExtDescriptor* ) + ( (uint8_t*)priv_data->vh + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + if (!hfsplus_update_vh(fs)) + return -1; + break; + + /************** BTREE *************/ + case CR_BTREE_CAT_JIB : + if (!hfsj_update_jib(fs, new_start)) + return -1; + goto BTREE_CAT; + + case CR_BTREE_CAT_JL : + if (!hfsj_update_jl(fs, new_start)) + return -1; + goto BTREE_CAT; + + BTREE_CAT: + case CR_BTREE_CAT : + file = priv_data->catalog_file; + goto CR_BTREE; + + case CR_BTREE_ATTR : + file = priv_data->attributes_file; + goto CR_BTREE; + + case CR_BTREE_EXT_ATTR : + if (priv_data->attributes_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->attributes_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_CAT : + if (priv_data->catalog_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->catalog_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_ALLOC : + if (priv_data->allocation_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->allocation_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_START : + /* No startup file opened */ + CR_BTREE_EXT : + case CR_BTREE_EXT_0 : + file = priv_data->extents_file; + + CR_BTREE : + PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block + > ref->ref_offset); + if (!hfsplus_file_read(file, hfsp_block, + (PedSector)ref->ref_block * ref->sect_by_block, + ref->sect_by_block)) + return -1; + extent = ( HfsPExtDescriptor* ) + ( hfsp_block + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + if (!hfsplus_file_write(file, hfsp_block, + (PedSector)ref->ref_block * ref->sect_by_block, + ref->sect_by_block) + || !ped_geometry_sync_fast (priv_data->plus_geom)) + return -1; + break; + + /********** BUG *********/ + default : + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("A reference to an extent comes from a place " + "it should not. You should check the file " + "system!")); + return -1; + break; + } + + move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); + if (!move) return -1; + PED_ASSERT(move == ref); + } + + return new_start; +} + +/* save any dirty sector of the allocation bitmap file */ +static int +hfsplus_save_allocation(PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int map_sectors, i, j; + int ret = 1; + + map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); + + for (i = 0; i < map_sectors;) { + for (j = i; + (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j)); + ++j) + CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j); + if (j-i) { + ret = hfsplus_file_write(priv_data->allocation_file, + priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT, + i, j-i) && ret; + i = j; + } else + ++i; + } + + return ret; +} + +/* This function moves an extent starting at block fblock + to block to_fblock if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occurred */ +/* Return 0 if no extent was found */ +static int +hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, + HfsCPrivateCache* cache) +{ + HfsCPrivateExtent* ref; + unsigned int old_start, new_start; + + ref = hfsc_cache_search_extent(cache, *ptr_fblock); + if (!ref) return 0; + + old_start = *ptr_fblock; + new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); + if (new_start == (unsigned)-1) return -1; + if (new_start > old_start) { + new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock, + cache, ref); + if (new_start == (unsigned)-1 || new_start > old_start) + return -1; + } + + hfsplus_save_allocation(fs); + return 1; +} + +static int +hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPExtDescriptor* extent; + unsigned int j; + + extent = priv_data->vh->allocation_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_ALLOC, + j ) + ) + return 0; + } + + extent = priv_data->vh->extents_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_EXT, + j ) + ) + return 0; + } + + extent = priv_data->vh->catalog_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_CAT, + j ) + ) + return 0; + } + + extent = priv_data->vh->attributes_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_ATTR, + j ) + ) + return 0; + } + + extent = priv_data->vh->startup_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_START, + j ) + ) + return 0; + } + + return 1; +} + +static int +hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPCatalogKey* catalog_key; + HfsPCatalog* catalog_data; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + uint32_t jib = priv_data->jib_start_block, + jl = priv_data->jl_start_block; + + if (!priv_data->catalog_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS+ volume has no catalog file. " + "This is very unusual!")); + return 1; + } + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256); + + node = (uint8_t*) ped_malloc(bsize); + if (!node) return 0; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->catalog_file, node, + (PedSector) leaf_node * size, size)) { + free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + uint8_t where; + + catalog_key = (HfsPCatalogKey*) + ( node + PED_BE16_TO_CPU (*((uint16_t *) + (node+(bsize - 2*i)))) ); + skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length) + + 1) & ~1; + catalog_data = (HfsPCatalog*) + (((uint8_t*)catalog_key) + skip); + /* check for obvious error in FS */ + if (((uint8_t*)catalog_key - node < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return 0; + } + + if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE) + continue; + + extent = catalog_data->sel.file.data_fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + where = CR_BTREE_CAT; + if ( PED_BE32_TO_CPU(extent[j].start_block) + == jib ) { + jib = 0; + where = CR_BTREE_CAT_JIB; + } else + if ( PED_BE32_TO_CPU(extent[j].start_block) + == jl ) { + jl = 0; + where = CR_BTREE_CAT_JL; + } + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + where, + j ) + ) { + free (node); + return 0; + } + } + + extent = catalog_data->sel.file.res_fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + CR_BTREE_CAT, + j ) + ) { + free (node); + return 0; + } + } + } + } + + free (node); + return 1; +} + +static int +hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPExtentKey* extent_key; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + + if (!priv_data->extents_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS+ volume has no extents overflow " + "file. This is quite unusual!")); + return 1; + } + + if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0)) + return 0; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256); + + node = (uint8_t*) ped_malloc (bsize); + if (!node) return -1; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->extents_file, node, + (PedSector) leaf_node * size, size)) { + free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + uint8_t where; + extent_key = (HfsPExtentKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + extent = (HfsPExtDescriptor*) + (((uint8_t*)extent_key) + sizeof (HfsPExtentKey)); + /* check for obvious error in FS */ + if (((uint8_t*)extent_key - node < HFS_FIRST_REC) + || ((uint8_t*)extent - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return -1; + } + + switch (extent_key->file_ID) { + case PED_CPU_TO_BE32 (HFS_XTENT_ID) : + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The extents overflow file should not" + " contain its own extents! You should " + "check the file system.")) + != PED_EXCEPTION_IGNORE) + return 0; + where = CR_BTREE_EXT_EXT; + break; + case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : + where = CR_BTREE_EXT_CAT; + break; + case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) : + where = CR_BTREE_EXT_ALLOC; + break; + case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) : + where = CR_BTREE_EXT_START; + break; + case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) : + where = CR_BTREE_EXT_ATTR; + break; + default : + where = CR_BTREE_EXT_0; + break; + } + + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + where, + j ) + ) { + free (node); + return 0; + } + } + } + } + + free (node); + return 1; +} + +static int +hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPPrivateGenericKey* generic_key; + HfsPForkDataAttr* fork_ext_data; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + + /* attributes file is facultative */ + if (!priv_data->attributes_file->sect_nb) + return 1; + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0)) + return 0; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256); + + node = (uint8_t*) ped_malloc(bsize); + if (!node) return 0; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->attributes_file, node, + (PedSector) leaf_node * size, size)) { + free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + generic_key = (HfsPPrivateGenericKey*) + (node + PED_BE16_TO_CPU(*((uint16_t *) + (node+(bsize - 2*i))))); + skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length) + + 1 ) & ~1; + fork_ext_data = (HfsPForkDataAttr*) + (((uint8_t*)generic_key) + skip); + /* check for obvious error in FS */ + if (((uint8_t*)generic_key - node < HFS_FIRST_REC) + || ((uint8_t*)fork_ext_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return 0; + } + + if (fork_ext_data->record_type + == PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) { + extent = fork_ext_data->fork_res.fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU ( + extent[j].start_block ), + PED_BE32_TO_CPU ( + extent[j].block_count ), + leaf_node, + (uint8_t*)extent-node, + size, + CR_BTREE_ATTR, + j ) + ) { + free(node); + return 0; + } + } + } else if (fork_ext_data->record_type + == PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) { + extent = fork_ext_data->fork_res.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU ( + extent[j].start_block ), + PED_BE32_TO_CPU ( + extent[j].block_count ), + leaf_node, + (uint8_t*)extent-node, + size, + CR_BTREE_ATTR, + j ) + ) { + free(node); + return 0; + } + } + } else continue; + } + } + + free (node); + return 1; +} + +static HfsCPrivateCache* +hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsCPrivateCache* ret; + unsigned int file_number, block_number; + + file_number = PED_BE32_TO_CPU(priv_data->vh->file_count); + block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks); + ret = hfsc_new_cache(block_number, file_number); + if (!ret) return NULL; + + if (!hfsplus_cache_from_vh(ret, fs, timer) || + !hfsplus_cache_from_catalog(ret, fs, timer) || + !hfsplus_cache_from_extent(ret, fs, timer) || + !hfsplus_cache_from_attributes(ret, fs, timer)) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not cache the file system in memory.")); + hfsc_delete_cache(ret); + return NULL; + } + + return ret; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + PedSector bytes_buff; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsCPrivateCache* cache; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int divisor = PED_BE32_TO_CPU (vh->total_blocks) + + 1 - start - to_free; + int ret; + + PED_ASSERT (!hfsp_block); + + cache = hfsplus_cache_extents (fs, timer); + if (!cache) + return 0; + + /* Calculate the size of the copy buffer : + * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF + * takes the maximum number of HFS blocks so that the buffer + * will remain smaller than or equal to BYTES_MAX_BUFF, with + * a minimum of 1 HFS block */ + bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size) + * (PedSector) BLOCK_MAX_BUFF; + if (bytes_buff > BYTES_MAX_BUFF) { + hfsp_block_count = BYTES_MAX_BUFF + / PED_BE32_TO_CPU (priv_data->vh->block_size); + if (!hfsp_block_count) + hfsp_block_count = 1; + bytes_buff = (PedSector) hfsp_block_count + * PED_BE32_TO_CPU (priv_data->vh->block_size); + } else + hfsp_block_count = BLOCK_MAX_BUFF; + + /* If the cache code requests more space, give it to him */ + if (bytes_buff < hfsc_cache_needed_buffer (cache)) + bytes_buff = hfsc_cache_needed_buffer (cache); + + hfsp_block = (uint8_t*) ped_malloc (bytes_buff); + if (!hfsp_block) + goto error_cache; + + if (!hfsplus_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks list could not be loaded.")); + goto error_alloc; + } + + while ( fblock < ( priv_data->plus_geom->length - 2 ) + / ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) ) { + if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock) + && (!hfsplus_is_bad_block (fs, fblock))) { + if (!(ret = hfsplus_move_extent_starting_at (fs, + &fblock, &to_fblock, cache))) + to_fblock = ++fblock; + else if (ret == -1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred during extent " + "relocation.")); + goto error_alloc; + } + } else { + fblock++; + } + + ped_timer_update(timer, (float)(to_fblock - start) / divisor); + } + + free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; + hfsc_delete_cache (cache); + return 1; + +error_alloc: + free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; +error_cache: + hfsc_delete_cache (cache); + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/hfs/reloc_plus.h b/libparted/fs/hfs/reloc_plus.h new file mode 100644 index 0000000..2b2e12c --- /dev/null +++ b/libparted/fs/hfs/reloc_plus.h @@ -0,0 +1,36 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _RELOC_PLUS_H +#define _RELOC_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsplus_update_vh (PedFileSystem *fs); + +int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free); + + +#endif /* _RELOC_PLUS_H */ diff --git a/libparted/libparted.a b/libparted/libparted.a Binary files differnew file mode 100644 index 0000000..4654f3f --- /dev/null +++ b/libparted/libparted.a diff --git a/libparted/libparted.c b/libparted/libparted.c new file mode 100644 index 0000000..a0d58a6 --- /dev/null +++ b/libparted/libparted.c @@ -0,0 +1,165 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2001, 2007-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> +#include "configmake.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#include "architecture.h" + +#if ENABLE_NLS +# include <locale.h> +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* ped_malloc() debugging. Stick the address and size of memory blocks that + * weren't free()d in here, and an exception will be thrown when it is + * allocated. That way, you can find out what, exactly, the allocated thing + * is, and where it is created. + */ +typedef struct +{ + void* pointer; + size_t size; +} pointer_size_type; + +/* IMHO, none of the DEBUG-related code below is useful, and the + ped_malloc memset code is actually quite harmful: it masked at + least two nasty bugs that were fixed in June of 2007. */ +#undef DEBUG +#ifdef DEBUG +static pointer_size_type dodgy_malloc_list[] = { + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} +}; + +static int dodgy_memory_active[100]; +#endif /* DEBUG */ + +#ifdef ENABLE_FS +extern void ped_file_system_hfs_init (void); +extern void ped_file_system_fat_init (void); + +static void +init_file_system_types () +{ + ped_file_system_hfs_init (); + ped_file_system_fat_init (); +} +#endif /* ENABLE_FS */ + +static void _init() __attribute__ ((constructor)); + +static void +_init() +{ +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE, LOCALEDIR); +#endif + +#ifdef ENABLE_FS + init_file_system_types (); +#endif + ped_set_architecture (); +#ifdef DEBUG + memset (dodgy_memory_active, 0, sizeof (dodgy_memory_active)); +#endif +} + +#ifdef ENABLE_FS +extern void ped_file_system_fat_done (void); +extern void ped_file_system_hfs_done (void); + +static void +done_file_system_types () +{ + ped_file_system_fat_done (); + ped_file_system_hfs_done (); +} +#endif /* ENABLE_FS */ + +static void _done() __attribute__ ((destructor)); + +static void +_done() +{ + ped_device_free_all (); + +#ifdef ENABLE_FS + done_file_system_types (); +#endif +} + +const char* +ped_get_version () +{ + return VERSION; +} + +void* +ped_malloc (size_t size) +{ + void* mem; + + mem = (void*) malloc (size); + if (!mem) { + ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, + _("Out of memory.")); + return NULL; + } + + return mem; +} + +int +ped_realloc (void** old, size_t size) +{ + void* mem; + + mem = (void*) realloc (*old, size); + if (!mem) { + ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, + _("Out of memory.")); + return 0; + } + *old = mem; + return 1; +} + + +void* ped_calloc (size_t size) +{ + void* buf = ped_malloc (size); + + memset (buf, 0, size); + + return buf; +} diff --git a/libparted/linux.c b/libparted/linux.c new file mode 100644 index 0000000..889f629 --- /dev/null +++ b/libparted/linux.c @@ -0,0 +1,2616 @@ +/* libparted - a library for manipulating disk partitions + Copyright (C) 1999-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define PROC_DEVICES_BUFSIZ 16384 + +#include <config.h> +#include "linux.h" +#include <linux/blkpg.h> +#include <parted/parted.h> +#include <parted/debug.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <syscall.h> +#include <unistd.h> +#include <stdbool.h> +#include <dirent.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> /* for uname() */ +#include <scsi/scsi.h> +#include <assert.h> + +#include "architecture.h" +#include "dirname.h" +#include "xstrtol.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#ifndef __NR__llseek +#define __NR__llseek 140 +#endif + +#ifndef SCSI_IOCTL_SEND_COMMAND +#define SCSI_IOCTL_SEND_COMMAND 1 +#endif + +/* from <linux/hdreg.h> */ +#define HDIO_GETGEO 0x0301 /* get device geometry */ +#define HDIO_GET_IDENTITY 0x030d /* get IDE identification info */ + +#define RD_MODE (O_RDONLY) +#define WR_MODE (O_WRONLY) +#define RW_MODE (O_RDWR) + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +struct ata7_sectinfo { + int valid1:1; + int valid2:1; + int rsv:26; + int multiplier:4; +}; + +/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */ +struct hd_driveid { + unsigned short config; /* lots of obsolete bit flags */ + unsigned short cyls; /* "physical" cyls */ + unsigned short reserved2; /* reserved (word 2) */ + unsigned short heads; /* "physical" heads */ + unsigned short track_bytes; /* unformatted bytes per track */ + unsigned short sector_bytes; /* unformatted bytes per sector */ + unsigned short sectors; /* "physical" sectors per track */ + unsigned short vendor0; /* vendor unique */ + unsigned short vendor1; /* vendor unique */ + unsigned short vendor2; /* vendor unique */ + unsigned char serial_no[20]; /* 0 = not_specified */ + unsigned short buf_type; + unsigned short buf_size; /* 512 byte increments; + 0 = not_specified */ + unsigned short ecc_bytes; /* for r/w long cmds; + 0 = not_specified */ + unsigned char fw_rev[8]; /* 0 = not_specified */ + char model[40]; /* 0 = not_specified */ + unsigned char max_multsect; /* 0=not_implemented */ + unsigned char vendor3; /* vendor unique */ + unsigned short dword_io; /* 0=not_implemented; 1=implemented */ + unsigned char vendor4; /* vendor unique */ + unsigned char capability; /* bits 0:DMA 1:LBA 2:IORDYsw + 3:IORDYsup*/ + unsigned short reserved50; /* reserved (word 50) */ + unsigned char vendor5; /* vendor unique */ + unsigned char tPIO; /* 0=slow, 1=medium, 2=fast */ + unsigned char vendor6; /* vendor unique */ + unsigned char tDMA; /* 0=slow, 1=medium, 2=fast */ + unsigned short field_valid; /* bits 0:cur_ok 1:eide_ok */ + unsigned short cur_cyls; /* logical cylinders */ + unsigned short cur_heads; /* logical heads */ + unsigned short cur_sectors; /* logical sectors per track */ + unsigned short cur_capacity0; /* logical total sectors on drive */ + unsigned short cur_capacity1; /* (2 words, misaligned int) */ + unsigned char multsect; /* current multiple sector count */ + unsigned char multsect_valid; /* when (bit0==1) multsect is ok */ + unsigned int lba_capacity; /* total number of sectors */ + unsigned short dma_1word; /* single-word dma info */ + unsigned short dma_mword; /* multiple-word dma info */ + unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */ + unsigned short eide_dma_min; /* min mword dma cycle time (ns) */ + unsigned short eide_dma_time; /* recommended mword dma cycle + time (ns) */ + unsigned short eide_pio; /* min cycle time (ns), no IORDY */ + unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */ + unsigned short words69_70[2]; /* reserved words 69-70 */ + /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */ + unsigned short words71_74[4]; /* reserved words 71-74 */ + unsigned short queue_depth; /* */ + unsigned short words76_79[4]; /* reserved words 76-79 */ + unsigned short major_rev_num; /* */ + unsigned short minor_rev_num; /* */ + unsigned short command_set_1; /* bits 0:Smart 1:Security 2:Removable + 3:PM */ + unsigned short command_set_2; /* bits 14:Smart Enabled 13:0 zero */ + unsigned short cfsse; /* command set-feature supported + extensions */ + unsigned short cfs_enable_1; /* command set-feature enabled */ + unsigned short cfs_enable_2; /* command set-feature enabled */ + unsigned short csf_default; /* command set-feature default */ + unsigned short dma_ultra; /* */ + unsigned short word89; /* reserved (word 89) */ + unsigned short word90; /* reserved (word 90) */ + unsigned short CurAPMvalues; /* current APM values */ + unsigned short word92; /* reserved (word 92) */ + unsigned short hw_config; /* hardware config */ + unsigned short words94_105[12];/* reserved words 94-105 */ + struct ata7_sectinfo ata7_sectinfo; /* ATAPI/ATA7 physical and logical + sector size */ + unsigned short words107_116[10];/* reserved words 107-116 */ + unsigned int logical_sectsize;/* ATAPI/ATA7 logical sector size */ + unsigned short words119_125[7];/* reserved words 119-125 */ + unsigned short last_lun; /* reserved (word 126) */ + unsigned short word127; /* reserved (word 127) */ + unsigned short dlf; /* device lock function + * 15:9 reserved + * 8 security level 1:max 0:high + * 7:6 reserved + * 5 enhanced erase + * 4 expire + * 3 frozen + * 2 locked + * 1 en/disabled + * 0 capability + */ + unsigned short csfo; /* current set features options + * 15:4 reserved + * 3 auto reassign + * 2 reverting + * 1 read-look-ahead + * 0 write cache + */ + unsigned short words130_155[26];/* reserved vendor words 130-155 */ + unsigned short word156; + unsigned short words157_159[3]; /* reserved vendor words 157-159 */ + unsigned short words160_255[95];/* reserved words 160-255 */ +}; + +/* from <linux/fs.h> */ +#define BLKRRPART _IO(0x12,95) /* re-read partition table */ +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#define BLKSSZGET _IO(0x12,104) /* get block device sector size */ +#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ +#define BLKSETLASTSECT _IO(0x12,109) /* set last sector of block device */ + +/* return device size in bytes (u64 *arg) */ +#define BLKGETSIZE64 _IOR(0x12,114,size_t) + +struct blkdev_ioctl_param { + unsigned int block; + size_t content_length; + char * block_contents; +}; + +/* from <linux/major.h> */ +#define IDE0_MAJOR 3 +#define IDE1_MAJOR 22 +#define IDE2_MAJOR 33 +#define IDE3_MAJOR 34 +#define IDE4_MAJOR 56 +#define IDE5_MAJOR 57 +#define SCSI_CDROM_MAJOR 11 +#define SCSI_DISK0_MAJOR 8 +#define SCSI_DISK1_MAJOR 65 +#define SCSI_DISK2_MAJOR 66 +#define SCSI_DISK3_MAJOR 67 +#define SCSI_DISK4_MAJOR 68 +#define SCSI_DISK5_MAJOR 69 +#define SCSI_DISK6_MAJOR 70 +#define SCSI_DISK7_MAJOR 71 +#define SCSI_DISK8_MAJOR 128 +#define SCSI_DISK9_MAJOR 129 +#define SCSI_DISK10_MAJOR 130 +#define SCSI_DISK11_MAJOR 131 +#define SCSI_DISK12_MAJOR 132 +#define SCSI_DISK13_MAJOR 133 +#define SCSI_DISK14_MAJOR 134 +#define SCSI_DISK15_MAJOR 135 +#define COMPAQ_SMART2_MAJOR 72 +#define COMPAQ_SMART2_MAJOR1 73 +#define COMPAQ_SMART2_MAJOR2 74 +#define COMPAQ_SMART2_MAJOR3 75 +#define COMPAQ_SMART2_MAJOR4 76 +#define COMPAQ_SMART2_MAJOR5 77 +#define COMPAQ_SMART2_MAJOR6 78 +#define COMPAQ_SMART2_MAJOR7 79 +#define COMPAQ_SMART_MAJOR 104 +#define COMPAQ_SMART_MAJOR1 105 +#define COMPAQ_SMART_MAJOR2 106 +#define COMPAQ_SMART_MAJOR3 107 +#define COMPAQ_SMART_MAJOR4 108 +#define COMPAQ_SMART_MAJOR5 109 +#define COMPAQ_SMART_MAJOR6 110 +#define COMPAQ_SMART_MAJOR7 111 +#define DAC960_MAJOR 48 +#define ATARAID_MAJOR 114 +#define I2O_MAJOR1 80 +#define I2O_MAJOR2 81 +#define I2O_MAJOR3 82 +#define I2O_MAJOR4 83 +#define I2O_MAJOR5 84 +#define I2O_MAJOR6 85 +#define I2O_MAJOR7 86 +#define I2O_MAJOR8 87 +#define UBD_MAJOR 98 +#define DASD_MAJOR 94 +#define VIODASD_MAJOR 112 +#define AOE_MAJOR 152 +#define SX8_MAJOR1 160 +#define SX8_MAJOR2 161 +#define XVD_MAJOR 202 +#define SDMMC_MAJOR 179 +#define LOOP_MAJOR 7 +#define MD_MAJOR 9 + +#define SCSI_BLK_MAJOR(M) ( \ + (M) == SCSI_DISK0_MAJOR \ + || (M) == SCSI_CDROM_MAJOR \ + || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \ + || ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR)) + +/* Maximum number of partitions supported by linux. */ +#define MAX_NUM_PARTS 64 + +static char* _device_get_part_path (PedDevice* dev, int num); +static int _partition_is_mounted_by_path (const char* path); + +static int +_read_fd (int fd, char **buf) +{ + char* p; + size_t size = PROC_DEVICES_BUFSIZ; + int s, filesize = 0; + + *buf = malloc (size * sizeof (char)); + if (*buf == 0) { + return -1; + } + + do { + p = &(*buf) [filesize]; + s = read (fd, p, PROC_DEVICES_BUFSIZ); + /* exit if there is an error or EOF is reached */ + if (s <= 0) + break; + filesize += s; + size += s; + char *new_buf = realloc (*buf, size); + if (new_buf == NULL) { + int saved_errno = errno; + free (*buf); + errno = saved_errno; + return -1; + } + *buf = new_buf; + } while (1); + + if (filesize == 0 && s < 0) { + free (*buf); + *buf = NULL; + return -1; + } else { + char *new_buf = realloc (*buf, filesize + 1); + if (new_buf == NULL) { + int saved_errno = errno; + free (*buf); + errno = saved_errno; + return -1; + } + *buf = new_buf; + (*buf)[filesize] = '\0'; + } + + return filesize; +} + +static int +_major_type_in_devices (int major, const char* type) +{ + int fd; + char* buf = NULL; + char* line; + char* end; + int bd = 0; + char c; + + fd = open ("/proc/devices", O_RDONLY); + if (fd < 0) + return 0; + + if (_read_fd(fd, &buf) < 0) { + close(fd); + return 0; + } + + line = buf; + end = strchr(line, '\n'); + while (end) { + char *name; + int maj; + + c = *end; + *end = '\0'; + + if (!bd) { + if (!strncmp(line, "Block devices:", 14)) + bd = 1; + goto next; + } + + name = strrchr(line, ' '); + if (!name || strcmp(name+1, type)) + goto next; + + maj = strtol(line, &name, 10); + if (maj == major) { + free(buf); + close(fd); + return 1; + } + +next: + *end = c; + line = end+1; + end = strchr(line, '\n'); + } + free(buf); + close(fd); + return 0; +} + +static int +_is_ide_major (int major) +{ + switch (major) { + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + case IDE4_MAJOR: + case IDE5_MAJOR: + return 1; + + default: + return 0; + } +} + +static int +_is_cpqarray_major (int major) +{ + return ((COMPAQ_SMART2_MAJOR <= major && major <= COMPAQ_SMART2_MAJOR7) + || (COMPAQ_SMART_MAJOR <= major && major <= COMPAQ_SMART_MAJOR7)); +} + +static int +_is_i2o_major (int major) +{ + return (I2O_MAJOR1 <= major && major <= I2O_MAJOR8); +} + +static int +_is_sx8_major (int major) +{ + return (SX8_MAJOR1 <= major && major <= SX8_MAJOR2); +} + +static int +_is_virtblk_major (int major) +{ + return _major_type_in_devices (major, "virtblk"); +} + +static int +_device_stat (PedDevice* dev, struct stat * dev_stat) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + while (1) { + if (!stat (dev->path, dev_stat)) { + return 1; + } else { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Could not stat device %s - %s."), + dev->path, + strerror (errno)) + != PED_EXCEPTION_RETRY) + return 0; + } + } +} + +static int +_device_probe_type (PedDevice* dev) +{ + struct stat dev_stat; + int dev_major; + int dev_minor; + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + if (!_device_stat (dev, &dev_stat)) + return 0; + + if (!S_ISBLK(dev_stat.st_mode)) { + dev->type = PED_DEVICE_FILE; + return 1; + } + + arch_specific->major = dev_major = major (dev_stat.st_rdev); + arch_specific->minor = dev_minor = minor (dev_stat.st_rdev); + + if (SCSI_BLK_MAJOR (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_SCSI; + } else if (_is_ide_major (dev_major) && (dev_minor % 0x40 == 0)) { + dev->type = PED_DEVICE_IDE; + } else if (dev_major == DAC960_MAJOR && (dev_minor % 0x8 == 0)) { + dev->type = PED_DEVICE_DAC960; + } else if (dev_major == ATARAID_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_ATARAID; + } else if (dev_major == AOE_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_AOE; + } else if (dev_major == DASD_MAJOR && (dev_minor % 0x4 == 0)) { + dev->type = PED_DEVICE_DASD; + } else if (dev_major == VIODASD_MAJOR && (dev_minor % 0x8 == 0)) { + dev->type = PED_DEVICE_VIODASD; + } else if (_is_sx8_major(dev_major) && (dev_minor % 0x20 == 0)) { + dev->type = PED_DEVICE_SX8; + } else if (_is_i2o_major (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_I2O; + } else if (_is_cpqarray_major (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_CPQARRAY; + } else if (dev_major == UBD_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_UBD; + } else if (dev_major == XVD_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_XVD; + } else if (dev_major == SDMMC_MAJOR && (dev_minor % 0x08 == 0)) { + dev->type = PED_DEVICE_SDMMC; + } else if (_is_virtblk_major(dev_major)) { + dev->type = PED_DEVICE_VIRTBLK; + } else if (dev_major == LOOP_MAJOR) { + dev->type = PED_DEVICE_FILE; + } else if (dev_major == MD_MAJOR) { + dev->type = PED_DEVICE_MD; + } else { + dev->type = PED_DEVICE_UNKNOWN; + } + + return 1; +} + +static int +_get_linux_version () +{ + static int kver = -1; + + struct utsname uts; + int major; + int minor; + int teeny; + + if (kver != -1) + return kver; + + if (uname (&uts)) + return kver = 0; + if (sscanf (uts.release, "%u.%u.%u", &major, &minor, &teeny) != 3) + return kver = 0; + + return kver = KERNEL_VERSION (major, minor, teeny); +} + +static int +_have_kern26 () +{ + static int have_kern26 = -1; + int kver; + + if (have_kern26 != -1) + return have_kern26; + + kver = _get_linux_version(); + return have_kern26 = kver >= KERNEL_VERSION (2,6,0) ? 1 : 0; +} + +#if USE_BLKID +static void +get_blkid_topology (LinuxSpecific *arch_specific) +{ + arch_specific->probe = blkid_new_probe (); + if (!arch_specific->probe) + return; + + if (blkid_probe_set_device(arch_specific->probe, + arch_specific->fd, 0, 0)) + return; + + arch_specific->topology = + blkid_probe_get_topology(arch_specific->probe); +} +#endif + +static void +_device_set_sector_size (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int sector_size; + + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + PED_ASSERT (dev->open_count); + + if (_get_linux_version() < KERNEL_VERSION (2,3,0)) { + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + return; + } + + if (ioctl (arch_specific->fd, BLKSSZGET, §or_size)) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Could not determine sector size for %s: %s.\n" + "Using the default sector size (%lld)."), + dev->path, strerror (errno), PED_SECTOR_SIZE_DEFAULT); + } else { + dev->sector_size = (long long)sector_size; + dev->phys_sector_size = dev->sector_size; + } + +#if USE_BLKID + get_blkid_topology(arch_specific); + if (!arch_specific->topology) { + dev->phys_sector_size = 0; + } else { + dev->phys_sector_size = + blkid_topology_get_physical_sector_size( + arch_specific->topology); + } + if (dev->phys_sector_size == 0) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Could not determine physical sector size for %s.\n" + "Using the logical sector size (%lld)."), + dev->path, dev->sector_size); + dev->phys_sector_size = dev->sector_size; + } +#endif + +#if defined __s390__ || defined __s390x__ + /* Return PED_SECTOR_SIZE_DEFAULT for DASDs. */ + if (dev->type == PED_DEVICE_DASD) { + arch_specific->real_sector_size = dev->sector_size; + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + } +#endif +} + +static int +_kernel_has_blkgetsize64(void) +{ + int version = _get_linux_version(); + + if (version >= KERNEL_VERSION (2,5,4)) return 1; + if (version < KERNEL_VERSION (2,5,0) && + version >= KERNEL_VERSION (2,4,18)) return 1; + return 0; +} + +/* TODO: do a binary search if BLKGETSIZE doesn't work?! */ +static PedSector +_device_get_length (PedDevice* dev) +{ + unsigned long size; + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + uint64_t bytes=0; + const char* test_str; + PedSector test_size; + + + PED_ASSERT (dev->open_count > 0); + PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + test_str = getenv ("PARTED_TEST_DEVICE_LENGTH"); + if (test_str + && xstrtoll (test_str, NULL, 10, &test_size, NULL) == LONGINT_OK) + return test_size; + + if (_kernel_has_blkgetsize64()) { + if (ioctl(arch_specific->fd, BLKGETSIZE64, &bytes) == 0) { + return bytes / dev->sector_size; + } + } + + if (ioctl (arch_specific->fd, BLKGETSIZE, &size)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unable to determine the size of %s (%s)."), + dev->path, + strerror (errno)); + return 0; + } + + return size; +} + +static int +_device_probe_geometry (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct stat dev_stat; + struct hd_geometry geometry; + + if (!_device_stat (dev, &dev_stat)) + return 0; + PED_ASSERT (S_ISBLK (dev_stat.st_mode)); + + _device_set_sector_size (dev); + + dev->length = _device_get_length (dev); + if (!dev->length) + return 0; + + /* The GETGEO ioctl is no longer useful (as of linux 2.6.x). We could + * still use it in 2.4.x, but this is contentious. Perhaps we should + * move to EDD. */ + dev->bios_geom.sectors = 63; + dev->bios_geom.heads = 255; + dev->bios_geom.cylinders + = dev->length / (63 * 255); + + /* FIXME: what should we put here? (TODO: discuss on linux-kernel) */ + if (!ioctl (arch_specific->fd, HDIO_GETGEO, &geometry) + && geometry.sectors && geometry.heads) { + dev->hw_geom.sectors = geometry.sectors; + dev->hw_geom.heads = geometry.heads; + dev->hw_geom.cylinders + = dev->length / (dev->hw_geom.heads + * dev->hw_geom.sectors); + } else { + dev->hw_geom = dev->bios_geom; + } + + return 1; +} + +static char* +strip_name(char* str) +{ + int i; + int end = 0; + + for (i = 0; str[i] != 0; i++) { + if (!isspace (str[i]) + || (isspace (str[i]) && !isspace (str[i+1]) && str[i+1])) { + str [end] = str[i]; + end++; + } + } + str[end] = 0; + return strdup (str); +} + +static int +init_ide (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct stat dev_stat; + struct hd_driveid hdi; + PedExceptionOption ex_status; + char hdi_buf[41]; + int sector_multiplier = 0; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!ped_device_open (dev)) + goto error; + + if (ioctl (arch_specific->fd, HDIO_GET_IDENTITY, &hdi)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Could not get identity of device %s - %s"), + dev->path, strerror (errno)); + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + dev->model = strdup(_("Generic IDE")); + break; + default: + PED_ASSERT (0); + break; + } + } else { + /* hdi.model is not guaranteed to be NULL terminated */ + memcpy (hdi_buf, hdi.model, 40); + hdi_buf[40] = '\0'; + dev->model = strip_name (hdi_buf); + + if (!hdi.ata7_sectinfo.valid1 && hdi.ata7_sectinfo.valid2) + sector_multiplier = hdi.ata7_sectinfo.multiplier; + else + sector_multiplier = 1; + + if (sector_multiplier != 1) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Device %s has multiple (%d) logical sectors " + "per physical sector.\n" + "GNU Parted supports this EXPERIMENTALLY for " + "some special disk label/file system " + "combinations, e.g. GPT and ext2/3.\n" + "Please consult the web site for up-to-date " + "information."), + dev->path, sector_multiplier); + + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + break; + default: + PED_ASSERT (0); + break; + } + } + + /* XXX sector_size has not been set yet! */ + /* dev->phys_sector_size = dev->sector_size + * sector_multiplier;*/ + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + if (!_device_probe_geometry (dev)) + goto error_close_dev; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +/* This function reads the /sys entry named "file" for device "dev". */ +static char * +read_device_sysfs_file (PedDevice *dev, const char *file) +{ + FILE *f; + char name_buf[128]; + char buf[256]; + + snprintf (name_buf, 127, "/sys/block/%s/device/%s", + last_component (dev->path), file); + + if ((f = fopen (name_buf, "r")) == NULL) + return NULL; + + if (fgets (buf, 255, f) == NULL) { + fclose (f); + return NULL; + } + + fclose (f); + return strip_name (buf); +} + +/* This function sends a query to a SCSI device for vendor and product + * information. It uses the deprecated SCSI_IOCTL_SEND_COMMAND to + * issue this query. + */ +static int +scsi_query_product_info (PedDevice* dev, char **vendor, char **product) +{ + /* The following are defined by the SCSI-2 specification. */ + typedef struct _scsi_inquiry_cmd + { + uint8_t op; + uint8_t lun; /* bits 5-7 denote the LUN */ + uint8_t page_code; + uint8_t reserved; + uint8_t alloc_length; + uint8_t control; + } __attribute__((packed)) scsi_inquiry_cmd_t; + + typedef struct _scsi_inquiry_data + { + uint8_t peripheral_info; + uint8_t device_info; + uint8_t version_info; + uint8_t _field1; + uint8_t additional_length; + uint8_t _reserved1; + uint8_t _reserved2; + uint8_t _field2; + uint8_t vendor_id[8]; + uint8_t product_id[16]; + uint8_t product_revision[4]; + uint8_t vendor_specific[20]; + uint8_t _reserved3[40]; + } __attribute__((packed)) scsi_inquiry_data_t; + + struct scsi_arg + { + unsigned int inlen; + unsigned int outlen; + + union arg_data + { + scsi_inquiry_data_t out; + scsi_inquiry_cmd_t in; + } data; + } arg; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + char buf[32]; + + *vendor = NULL; + *product = NULL; + + memset (&arg, 0x00, sizeof(struct scsi_arg)); + arg.inlen = 0; + arg.outlen = sizeof(scsi_inquiry_data_t); + arg.data.in.op = INQUIRY; + arg.data.in.lun = dev->host << 5; + arg.data.in.alloc_length = sizeof(scsi_inquiry_data_t); + arg.data.in.page_code = 0; + arg.data.in.reserved = 0; + arg.data.in.control = 0; + + if (ioctl (arch_specific->fd, SCSI_IOCTL_SEND_COMMAND, &arg) < 0) + return 0; + + memcpy (buf, arg.data.out.vendor_id, 8); + buf[8] = '\0'; + *vendor = strip_name (buf); + + memcpy (buf, arg.data.out.product_id, 16); + buf[16] = '\0'; + *product = strip_name (buf); + + return 1; +} + +/* This function provides the vendor and product name for a SCSI device. + * It supports both the modern /sys interface and direct queries + * via the deprecated ioctl, SCSI_IOCTL_SEND_COMMAND. + */ +static int +scsi_get_product_info (PedDevice* dev, char **vendor, char **product) +{ + *vendor = read_device_sysfs_file (dev, "vendor"); + *product = read_device_sysfs_file (dev, "model"); + if (*vendor && *product) + return 1; + + return scsi_query_product_info (dev, vendor, product); +} + +static int +init_scsi (PedDevice* dev) +{ + struct scsi_idlun + { + uint32_t dev_id; + uint32_t host_unique_id; + } idlun; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + char* vendor; + char* product; + + if (!ped_device_open (dev)) + goto error; + + if (ioctl (arch_specific->fd, SCSI_IOCTL_GET_IDLUN, &idlun) < 0) { + dev->host = 0; + dev->did = 0; + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Error initialising SCSI device %s - %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_IGNORE) + goto error_close_dev; + if (!_device_probe_geometry (dev)) + goto error_close_dev; + ped_device_close (dev); + return 1; + } + + dev->host = idlun.host_unique_id; + dev->did = idlun.dev_id; + + dev->model = (char*) ped_malloc (8 + 16 + 2); + if (!dev->model) + goto error_close_dev; + + if (scsi_get_product_info (dev, &vendor, &product)) { + sprintf (dev->model, "%.8s %.16s", vendor, product); + free (vendor); + free (product); + } else { + strcpy (dev->model, "Generic SCSI"); + } + + if (!_device_probe_geometry (dev)) + goto error_close_dev; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static int +init_file (PedDevice* dev) +{ + struct stat dev_stat; + + if (!_device_stat (dev, &dev_stat)) + goto error; + if (!ped_device_open (dev)) + goto error; + + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + char *p = getenv ("PARTED_SECTOR_SIZE"); + if (p) { + int s = atoi (p); + if (0 < s && s % 512 == 0) + dev->sector_size = s; + } + dev->phys_sector_size = dev->sector_size; + + if (S_ISBLK(dev_stat.st_mode)) + dev->length = _device_get_length (dev); + else + dev->length = dev_stat.st_size / dev->sector_size; + if (dev->length <= 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The device %s is so small that it cannot possibly " + "store a file system or partition table. Perhaps " + "you selected the wrong device?"), + dev->path); + goto error_close_dev; + } + + ped_device_close (dev); + + dev->bios_geom.cylinders = dev->length / 4 / 32; + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->hw_geom = dev->bios_geom; + dev->model = strdup (""); + + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +#if defined __s390__ || defined __s390x__ +static int +init_dasd (PedDevice* dev, const char* model_name) +{ + struct stat dev_stat; + struct hd_geometry geo; + dasd_information_t dasd_info; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!ped_device_open (dev)) + goto error; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + PED_ASSERT (S_ISBLK (dev_stat.st_mode)); + + _device_set_sector_size (dev); + if (!dev->sector_size) + goto error_close_dev; + + dev->length = _device_get_length (dev); + if (!dev->length) + goto error_close_dev; + + if (!ioctl (arch_specific->fd, HDIO_GETGEO, &geo)) { + dev->hw_geom.sectors = geo.sectors; + dev->hw_geom.heads = geo.heads; + dev->hw_geom.cylinders = dev->length + / (dev->hw_geom.heads * dev->hw_geom.sectors) + / (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->bios_geom = dev->hw_geom; + } else { + dev->bios_geom.sectors = 12; + dev->bios_geom.heads = 15; + dev->bios_geom.cylinders = dev->length + / (dev->hw_geom.heads * dev->hw_geom.sectors) + / (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->hw_geom = dev->bios_geom; + } + + if (!ioctl(arch_specific->fd, BIODASDINFO, &dasd_info)) { + arch_specific->devno = dasd_info.devno; + } else { + arch_specific->devno = arch_specific->major * 256 + + arch_specific->minor; + } + + dev->model = strdup (model_name); + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} +#endif + +static int +init_generic (PedDevice* dev, const char* model_name) +{ + struct stat dev_stat; + PedExceptionOption ex_status; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!ped_device_open (dev)) + goto error; + + ped_exception_fetch_all (); + if (_device_probe_geometry (dev)) { + ped_exception_leave_all (); + } else { + if (!_device_get_length (dev)) { + ped_exception_catch (); + ped_exception_leave_all (); + goto error_close_dev; + } + + /* hack to allow use of files, for testing */ + ped_exception_catch (); + ped_exception_leave_all (); + + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Unable to determine geometry of " + "file/device %s. You should not use Parted " + "unless you REALLY know what you're doing!"), + dev->path); + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_IGNORE: + break; + default: + PED_ASSERT (0); + break; + } + + /* what should we stick in here? */ + dev->length = dev_stat.st_size / PED_SECTOR_SIZE_DEFAULT; + dev->bios_geom.cylinders = dev->length / 4 / 32; + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + dev->model = strdup (model_name); + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static int +sdmmc_get_product_info (PedDevice* dev, char **type, char **name) +{ + *type = read_device_sysfs_file (dev, "type"); + *name = read_device_sysfs_file (dev, "name"); + if (*type && *name) + return 1; + + return 0; +} + +static int +init_sdmmc (PedDevice* dev) +{ + char id[128]; + char *type, *name; + + if (sdmmc_get_product_info (dev, &type, &name)) { + snprintf (id, sizeof(id) - 1, "%s %s", type, name); + free (type); + free (name); + } else { + snprintf (id, sizeof(id) - 1, "%s", + _("Generic SD/MMC Storage Card")); + } + return init_generic(dev, id); +} + +static PedDevice* +linux_new (const char* path) +{ + PedDevice* dev; + LinuxSpecific* arch_specific; + + PED_ASSERT (path != NULL); + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup (path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (LinuxSpecific*) ped_malloc (sizeof (LinuxSpecific)); + if (!dev->arch_specific) + goto error_free_path; + arch_specific = LINUX_SPECIFIC (dev); + arch_specific->dmtype = NULL; +#if USE_BLKID + arch_specific->probe = NULL; + arch_specific->topology = NULL; +#endif + + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + + if (!_device_probe_type (dev)) + goto error_free_arch_specific; + + switch (dev->type) { + case PED_DEVICE_IDE: + if (!init_ide (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SCSI: + if (!init_scsi (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_DAC960: + if (!init_generic (dev, _("DAC960 RAID controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SX8: + if (!init_generic (dev, _("Promise SX8 SATA Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_AOE: + if (!init_generic (dev, _("ATA over Ethernet Device"))) + goto error_free_arch_specific; + break; + +#if defined __s390__ || defined __s390x__ + case PED_DEVICE_DASD: + if (!init_dasd (dev, _("IBM S390 DASD drive"))) + goto error_free_arch_specific; + break; +#endif + + case PED_DEVICE_VIODASD: + if (!init_generic (dev, _("IBM iSeries Virtual DASD"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_CPQARRAY: + if (!init_generic (dev, _("Compaq Smart Array"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_ATARAID: + if (!init_generic (dev, _("ATARAID Controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_I2O: + if (!init_generic (dev, _("I2O Controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_UBD: + if (!init_generic (dev, _("User-Mode Linux UBD"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_FILE: + if (!init_file (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_DM: + { + char* type; + if (arch_specific->dmtype == NULL + || asprintf(&type, _("Linux device-mapper (%s)"), + arch_specific->dmtype) == -1) + goto error_free_arch_specific; + bool ok = init_generic (dev, type); + free (type); + if (!ok) + goto error_free_arch_specific; + break; + } + + case PED_DEVICE_XVD: + if (!init_generic (dev, _("Xen Virtual Block Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_UNKNOWN: + if (!init_generic (dev, _("Unknown"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SDMMC: + if (!init_sdmmc (dev)) + goto error_free_arch_specific; + break; + case PED_DEVICE_VIRTBLK: + if (!init_generic(dev, _("Virtio Block Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_MD: + if (!init_generic(dev, _("Linux Software RAID Array"))) + goto error_free_arch_specific; + break; + + default: + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("ped_device_new() Unsupported device type")); + goto error_free_arch_specific; + } + return dev; + +error_free_arch_specific: + free (dev->arch_specific); +error_free_path: + free (dev->path); +error_free_dev: + free (dev); +error: + return NULL; +} + +static void +linux_destroy (PedDevice* dev) +{ + LinuxSpecific *arch_specific = LINUX_SPECIFIC(dev); + void *p = arch_specific->dmtype; + +#if USE_BLKID + if (arch_specific->probe) + blkid_free_probe(arch_specific->probe); +#endif + free (p); + free (dev->arch_specific); + free (dev->path); + free (dev->model); + free (dev); +} + +static int +linux_is_busy (PedDevice* dev) +{ + int i; + char* part_name; + + if (_partition_is_mounted_by_path (dev->path)) + return 1; + + for (i = 0; i < 32; i++) { + int status; + + part_name = _device_get_part_path (dev, i); + if (!part_name) + return 1; + status = _partition_is_mounted_by_path (part_name); + free (part_name); + + if (status) + return 1; + } + + return 0; +} + +/* we need to flush the master device, and with kernel < 2.6 all the partition + * devices, because there is no coherency between the caches with old kernels. + * We should only flush unmounted partition devices, because: + * - there is never a need to flush them (we're not doing IO there) + * - flushing a device that is mounted causes unnecessary IO, and can + * even screw journaling & friends up. Even cause oopsen! + */ +static void +_flush_cache (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int i; + + if (dev->read_only) + return; + dev->dirty = 0; + + ioctl (arch_specific->fd, BLKFLSBUF); + + /* With linux-2.6.0 and newer, we're done. */ + if (_have_kern26()) + return; + + for (i = 1; i < 16; i++) { + char* name; + int fd; + + name = _device_get_part_path (dev, i); + if (!name) + break; + if (!_partition_is_mounted_by_path (name)) { + fd = open (name, WR_MODE, 0); + if (fd > 0) { + ioctl (fd, BLKFLSBUF); +retry: + if (fsync (fd) < 0 || close (fd) < 0) + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY + + PED_EXCEPTION_IGNORE, + _("Error fsyncing/closing %s: %s"), + name, strerror (errno)) + == PED_EXCEPTION_RETRY) + goto retry; + } + } + free (name); + } +} + +static int +linux_open (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + +retry: + arch_specific->fd = open (dev->path, RW_MODE); + + if (arch_specific->fd == -1) { + char* rw_error_msg = strerror (errno); + + arch_specific->fd = open (dev->path, RD_MODE); + + if (arch_specific->fd == -1) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_RETRY) { + return 0; + } else { + goto retry; + } + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, rw_error_msg, dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + /* With kernels < 2.6 flush cache for cache coherence issues */ + if (!_have_kern26()) + _flush_cache (dev); + + return 1; +} + +static int +linux_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +linux_close (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + if (dev->dirty) + _flush_cache (dev); +retry: + if (fsync (arch_specific->fd) < 0 || close (arch_specific->fd) < 0) + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY + PED_EXCEPTION_IGNORE, + _("Error fsyncing/closing %s: %s"), + dev->path, strerror (errno)) + == PED_EXCEPTION_RETRY) + goto retry; + return 1; +} + +static int +linux_refresh_close (PedDevice* dev) +{ + if (dev->dirty) + _flush_cache (dev); + return 1; +} + +#if SIZEOF_OFF_T < 8 + +static _syscall5(int,_llseek, + unsigned int, fd, + unsigned long, offset_high, + unsigned long, offset_low, + loff_t*, result, + unsigned int, origin) + +loff_t +llseek (unsigned int fd, loff_t offset, unsigned int whence) +{ + loff_t result; + int retval; + + retval = _llseek(fd, + ((unsigned long long)offset) >> 32, + ((unsigned long long)offset) & 0xffffffff, + &result, + whence); + return (retval==-1 ? (loff_t) retval : result); +} + +#endif /* SIZEOF_OFF_T < 8 */ + +static int +_device_seek (const PedDevice* dev, PedSector sector) +{ + LinuxSpecific* arch_specific; + + PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + arch_specific = LINUX_SPECIFIC (dev); + +#if SIZEOF_OFF_T < 8 + if (sizeof (off_t) < 8) { + loff_t pos = (loff_t)(sector * dev->sector_size); + return llseek (arch_specific->fd, pos, SEEK_SET) == pos; + } else +#endif + { + off_t pos = sector * dev->sector_size; + return lseek (arch_specific->fd, pos, SEEK_SET) == pos; + } +} + +static int +_read_lastoddsector (const PedDevice* dev, void* buffer) +{ + LinuxSpecific* arch_specific; + struct blkdev_ioctl_param ioctl_param; + + PED_ASSERT(dev != NULL); + PED_ASSERT(buffer != NULL); + + arch_specific = LINUX_SPECIFIC (dev); + +retry: + ioctl_param.block = 0; /* read the last sector */ + ioctl_param.content_length = dev->sector_size; + ioctl_param.block_contents = buffer; + + if (ioctl(arch_specific->fd, BLKGETLASTSECT, &ioctl_param) == -1) { + PedExceptionOption opt; + opt = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (errno), dev->path); + + if (opt == PED_EXCEPTION_CANCEL) + return 0; + if (opt == PED_EXCEPTION_RETRY) + goto retry; + } + + return 1; +} + +static int +linux_read (const PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedExceptionOption ex_status; + void* diobuf = NULL; + + PED_ASSERT (dev != NULL); + PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (_get_linux_version() < KERNEL_VERSION (2,6,0)) { + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + */ + if (dev->type != PED_DEVICE_FILE && (dev->length & 1) + && start + count - 1 == dev->length - 1) + return ped_device_read (dev, buffer, start, count - 1) + && _read_lastoddsector ( + dev, (char *) buffer + (count-1) * 512); + } + while (1) { + if (_device_seek (dev, start)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for read on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + default: + PED_ASSERT (0); + break; + } + } + + size_t read_length = count * dev->sector_size; + if (posix_memalign (&diobuf, dev->sector_size, read_length) != 0) + return 0; + + while (1) { + ssize_t status = read (arch_specific->fd, diobuf, read_length); + if (status > 0) + memcpy(buffer, diobuf, status); + if (status == (ssize_t) read_length) + break; + if (status > 0) { + read_length -= status; + buffer = (char *) buffer + status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + (status == 0 + ? _("end of file while reading %s") + : _("%s during read on %s")), + strerror (errno), + dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free(diobuf); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + free(diobuf); + return 0; + default: + PED_ASSERT (0); + break; + } + } + + free (diobuf); + + return 1; +} + +static int +_write_lastoddsector (PedDevice* dev, const void* buffer) +{ + LinuxSpecific* arch_specific; + struct blkdev_ioctl_param ioctl_param; + + PED_ASSERT(dev != NULL); + PED_ASSERT(buffer != NULL); + + arch_specific = LINUX_SPECIFIC (dev); + +retry: + ioctl_param.block = 0; /* write the last sector */ + ioctl_param.content_length = dev->sector_size; + ioctl_param.block_contents = (void*) buffer; + + if (ioctl(arch_specific->fd, BLKSETLASTSECT, &ioctl_param) == -1) { + PedExceptionOption opt; + opt = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + if (opt == PED_EXCEPTION_CANCEL) + return 0; + if (opt == PED_EXCEPTION_RETRY) + goto retry; + } + + return 1; +} + +static int +linux_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedExceptionOption ex_status; + void* diobuf; + void* diobuf_start; + + PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (dev->read_only) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + + if (_get_linux_version() < KERNEL_VERSION (2,6,0)) { + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + */ + if (dev->type != PED_DEVICE_FILE && (dev->length & 1) + && start + count - 1 == dev->length - 1) + return ped_device_write (dev, buffer, start, count - 1) + && _write_lastoddsector ( + dev, ((char*) buffer + + (count-1) * dev->sector_size)); + } + while (1) { + if (_device_seek (dev, start)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + default: + PED_ASSERT (0); + break; + } + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + size_t write_length = count * dev->sector_size; + dev->dirty = 1; + if (posix_memalign(&diobuf, dev->sector_size, write_length) != 0) + return 0; + memcpy(diobuf, buffer, write_length); + diobuf_start = diobuf; + while (1) { + ssize_t status = write (arch_specific->fd, diobuf, write_length); + if (status == write_length) break; + if (status > 0) { + write_length -= status; + diobuf = (char *) diobuf + status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free(diobuf_start); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + free(diobuf_start); + return 0; + default: + PED_ASSERT (0); + break; + } + } + free(diobuf_start); +#endif /* !READ_ONLY */ + return 1; +} + +/* returns the number of sectors that are ok. + */ +static PedSector +linux_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedSector done = 0; + int status; + void* diobuf; + + PED_ASSERT(dev != NULL); + + if (!_device_seek (dev, start)) + return 0; + + if (posix_memalign(&diobuf, PED_SECTOR_SIZE_DEFAULT, + count * PED_SECTOR_SIZE_DEFAULT) != 0) + return 0; + + for (done = 0; done < count; done += status / dev->sector_size) { + status = read (arch_specific->fd, diobuf, + (size_t) ((count-done) * dev->sector_size)); + if (status > 0) + memcpy(buffer, diobuf, status); + if (status < 0) + break; + } + free(diobuf); + + return done; +} + +static int +_do_fsync (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int status; + PedExceptionOption ex_status; + + while (1) { + status = fsync (arch_specific->fd); + if (status >= 0) break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + default: + PED_ASSERT (0); + break; + } + } + return 1; +} + +static int +linux_sync (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + if (dev->read_only) + return 1; + if (!_do_fsync (dev)) + return 0; + _flush_cache (dev); + return 1; +} + +static int +linux_sync_fast (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + if (dev->read_only) + return 1; + if (!_do_fsync (dev)) + return 0; + /* no cache flush... */ + return 1; +} + +static inline int +_compare_digit_state (char ch, int need_digit) +{ + return !!isdigit (ch) == need_digit; +} + +/* matches the regexp "[^0-9]+[0-9]+[^0-9]+[0-9]+$". + * Motivation: accept devices looking like /dev/rd/c0d0, but + * not looking like /dev/hda1 and /dev/rd/c0d0p1 + */ +static int +_match_rd_device (const char* name) +{ + const char* pos; + int state; + + /* exclude directory names from test */ + pos = strrchr(name, '/') ?: name; + + /* states: + * 0 non-digits + * 1 digits + * 2 non-digits + * 3 digits + */ + for (state = 0; state < 4; state++) { + int want_digits = (state % 2 == 1); + do { + if (!*pos) + return 0; + if (!_compare_digit_state (*pos, want_digits)) + return 0; + pos++; + } while (_compare_digit_state (*pos, want_digits)); + } + + return *pos == 0; +} + +static int +_probe_proc_partitions () +{ + FILE* proc_part_file; + int major, minor, size; + char buf [512]; + char part_name [256]; + char dev_name [256]; + int ok = 0; + + proc_part_file = fopen ("/proc/partitions", "r"); + if (!proc_part_file) + return 0; + + if (fgets (buf, 256, proc_part_file) == NULL) + goto done; + + if (fgets (buf, 256, proc_part_file) == NULL) + goto done; + + while (fgets (buf, 512, proc_part_file) + && sscanf (buf, "%d %d %d %255s", &major, &minor, &size, + part_name) == 4) { + /* Heuristic for telling partitions and devices apart + * Probably needs to be improved + */ + if (!_match_rd_device (part_name) + && isdigit (part_name [strlen (part_name) - 1])) + continue; + + strcpy (dev_name, "/dev/"); + strcat (dev_name, part_name); + _ped_device_probe (dev_name); + } + + ok = 1; + done: + fclose (proc_part_file); + return ok; +} + +struct _entry { + const char *name; + size_t len; +}; + +static int +_skip_entry (const char *name) +{ + struct _entry *i; + static struct _entry entries[] = { + { ".", sizeof (".") - 1 }, + { "..", sizeof ("..") - 1 }, + { "dm-", sizeof ("dm-") - 1 }, + { "loop", sizeof ("loop") - 1 }, + { "ram", sizeof ("ram") - 1 }, + { 0, 0 }, + }; + + for (i = entries; i->name != 0; i++) { + if (strncmp (name, i->name, i->len) == 0) + return 1; + } + + return 0; +} + +static int +_probe_sys_block () +{ + DIR *blockdir; + struct dirent *dirent; + char dev_name [256]; + char *ptr; + + if (!(blockdir = opendir ("/sys/block"))) + return 0; + while ((dirent = readdir (blockdir))) { + if (_skip_entry (dirent->d_name)) + continue; + + if (strlen (dirent->d_name) > sizeof (dev_name) - 6) + continue; /* device name too long! */ + + strcpy (dev_name, "/dev/"); + strcat (dev_name, dirent->d_name); + /* in /sys/block, '/'s are replaced with '!' or '.' */ + for (ptr = dev_name; *ptr != '\0'; ptr++) { + if (*ptr == '!' || *ptr == '.') + *ptr = '/'; + } + _ped_device_probe (dev_name); + } + + closedir (blockdir); + return 1; +} + +static int +_probe_standard_devices () +{ + _ped_device_probe ("/dev/hda"); + _ped_device_probe ("/dev/hdb"); + _ped_device_probe ("/dev/hdc"); + _ped_device_probe ("/dev/hdd"); + _ped_device_probe ("/dev/hde"); + _ped_device_probe ("/dev/hdf"); + _ped_device_probe ("/dev/hdg"); + _ped_device_probe ("/dev/hdh"); + + _ped_device_probe ("/dev/sda"); + _ped_device_probe ("/dev/sdb"); + _ped_device_probe ("/dev/sdc"); + _ped_device_probe ("/dev/sdd"); + _ped_device_probe ("/dev/sde"); + _ped_device_probe ("/dev/sdf"); + + return 1; +} + +static void +linux_probe_all () +{ + /* we should probe the standard devs too, even with /proc/partitions, + * because /proc/partitions might return devfs stuff, and we might not + * have devfs available + */ + _probe_standard_devices (); + + /* /sys/block is more reliable and consistent; fall back to using + * /proc/partitions if the former is unavailable, however. + */ + if (!_probe_sys_block ()) + _probe_proc_partitions (); +} + +static char * +zasprintf (const char *format, ...) +{ + va_list args; + char *resultp; + va_start (args, format); + int r = vasprintf (&resultp, format, args); + va_end (args); + return r < 0 ? NULL : resultp; +} + +static char* +_device_get_part_path (PedDevice *dev, int num) +{ + size_t path_len = strlen (dev->path); + + char *result; + /* Check for devfs-style /disc => /partN transformation + unconditionally; the system might be using udev with devfs rules, + and if not the test is harmless. */ + if (5 < path_len && !strcmp (dev->path + path_len - 5, "/disc")) { + /* replace /disc with /part%d */ + result = zasprintf ("%.*s/part%d", + (int) (path_len - 5), dev->path, num); + } else { + char const *p = (dev->type == PED_DEVICE_DAC960 + || dev->type == PED_DEVICE_CPQARRAY + || dev->type == PED_DEVICE_ATARAID + || dev->type == PED_DEVICE_DM + || isdigit (dev->path[path_len - 1]) + ? "p" : ""); + result = zasprintf ("%s%s%d", dev->path, p, num); + } + + return result; +} + +static char* +linux_partition_get_path (const PedPartition* part) +{ + return _device_get_part_path (part->disk->dev, part->num); +} + +static int +_mount_table_search (const char* file_name, dev_t dev) +{ + struct stat part_stat; + char line[512]; + char part_name[512]; + FILE* file; + + file = fopen (file_name, "r"); + if (!file) + return 0; + while (fgets (line, 512, file)) { + if (sscanf (line, "%s", part_name) == 1 + && stat (part_name, &part_stat) == 0) { + if (part_stat.st_rdev == dev) { + fclose (file); + return 1; + } + } + } + fclose (file); + return 0; +} + +static int +_partition_is_mounted_by_dev (dev_t dev) +{ + return _mount_table_search( "/proc/mounts", dev) + || _mount_table_search( "/proc/swaps", dev) + || _mount_table_search( "/etc/mtab", dev); +} + +static int +_partition_is_mounted_by_path (const char *path) +{ + struct stat part_stat; + if (stat (path, &part_stat) != 0) + return 0; + if (!S_ISBLK(part_stat.st_mode)) + return 0; + return _partition_is_mounted_by_dev (part_stat.st_rdev); +} + +/* If partition PART is mounted, or if we encounter an out-of-memory error + while trying to determine its status, return 1. Otherwise, return 0. */ +static int +_partition_is_mounted (const PedPartition *part) +{ + if (!ped_partition_is_active (part)) + return 0; + char *part_name = _device_get_part_path (part->disk->dev, part->num); + if (!part_name) + return 1; + int status = _partition_is_mounted_by_path (part_name); + free (part_name); + return !!status; +} + +static int +_has_partitions (const PedDisk* disk) +{ + PED_ASSERT(disk != NULL); + + /* Some devices can't be partitioned. */ + if (!strcmp (disk->type->name, "loop")) + return 0; + + return 1; +} + +static int +linux_partition_is_busy (const PedPartition* part) +{ + PedPartition* walk; + + PED_ASSERT (part != NULL); + + if (_partition_is_mounted (part)) + return 1; + if (part->type == PED_PARTITION_EXTENDED) { + for (walk = part->part_list; walk; walk = walk->next) { + if (linux_partition_is_busy (walk)) + return 1; + } + } + return 0; +} + +static int +_blkpg_part_command (PedDevice* dev, struct blkpg_partition* part, int op) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct blkpg_ioctl_arg ioctl_arg; + + ioctl_arg.op = op; + ioctl_arg.flags = 0; + ioctl_arg.datalen = sizeof (struct blkpg_partition); + ioctl_arg.data = (void*) part; + + return ioctl (arch_specific->fd, BLKPG, &ioctl_arg) == 0; +} + +static int +_blkpg_add_partition (PedDisk* disk, const PedPartition *part) +{ + struct blkpg_partition linux_part; + const char* vol_name; + char* dev_name; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (!_has_partitions (disk)) + return 0; + + if (ped_disk_type_check_feature (disk->type, + PED_DISK_TYPE_PARTITION_NAME)) + vol_name = ped_partition_get_name (part); + else + vol_name = NULL; + + dev_name = _device_get_part_path (disk->dev, part->num); + if (!dev_name) + return 0; + + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.start = part->geom.start * disk->dev->sector_size; + /* see fs/partitions/msdos.c:msdos_partition(): "leave room for LILO" */ + if (part->type & PED_PARTITION_EXTENDED) + linux_part.length = part->geom.length == 1 ? 512 : 1024; + else + linux_part.length = part->geom.length * disk->dev->sector_size; + linux_part.pno = part->num; + strncpy (linux_part.devname, dev_name, BLKPG_DEVNAMELTH); + if (vol_name) + strncpy (linux_part.volname, vol_name, BLKPG_VOLNAMELTH); + + free (dev_name); + + if (!_blkpg_part_command (disk->dev, &linux_part, + BLKPG_ADD_PARTITION)) { + return ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Error informing the kernel about modifications to " + "partition %s -- %s. This means Linux won't know " + "about any changes you made to %s until you reboot " + "-- so you shouldn't mount it or use it in any way " + "before rebooting."), + linux_part.devname, + strerror (errno), + linux_part.devname) + == PED_EXCEPTION_IGNORE; + } + + return 1; +} + +static int +_blkpg_remove_partition (PedDisk* disk, int n) +{ + struct blkpg_partition linux_part; + + if (!_has_partitions (disk)) + return 0; + + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.pno = n; + return _blkpg_part_command (disk->dev, &linux_part, + BLKPG_DEL_PARTITION); +} + +/* + * The number of partitions that a device can have depends on the kernel. + * If we don't find this value in /sys/block/DEV/range, we will use our own + * value. + */ +static unsigned int +_device_get_partition_range(PedDevice* dev) +{ + int range, r; + char path[128]; + FILE* fp; + bool ok; + + r = snprintf(path, sizeof(path), "/sys/block/%s/range", + last_component(dev->path)); + if (r < 0 || r >= sizeof(path)) + return MAX_NUM_PARTS; + + fp = fopen(path, "r"); + if (!fp) + return MAX_NUM_PARTS; + + ok = fscanf(fp, "%d", &range) == 1; + fclose(fp); + + /* (range <= 0) is none sense.*/ + return ok && range > 0 ? range : MAX_NUM_PARTS; +} + +/* + * Sync the partition table in two step process: + * 1. Remove all of the partitions from the kernel's tables, but do not attempt + * removal of any partition for which the corresponding ioctl call fails. + * 2. Add all the partitions that we hold in disk, throwing a warning + * if we cannot because step 1 failed to remove it and it is not being + * added back with the same start and length. + * + * To achieve this two step process we must calculate the minimum number of + * maximum possible partitions between what linux supports and what the label + * type supports. EX: + * + * number=MIN(max_parts_supported_in_linux,max_parts_supported_in_msdos_tables) + */ +static int +_disk_sync_part_table (PedDisk* disk) +{ + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + int lpn; + + /* lpn = largest partition number. */ + if (ped_disk_get_max_supported_partition_count(disk, &lpn)) + lpn = PED_MIN(lpn, _device_get_partition_range(disk->dev)); + else + lpn = _device_get_partition_range(disk->dev); + + /* Its not possible to support largest_partnum < 0. + * largest_partnum == 0 would mean does not support partitions. + * */ + if (lpn < 0) + return 0; + int ret = 0; + int *ok = calloc (lpn, sizeof *ok); + if (!ok) + return 0; + int *errnums = ped_malloc(sizeof(int) * lpn); + if (!errnums) + goto cleanup; + + /* Attempt to remove each and every partition, retrying for + up to max_sleep_seconds upon any failure due to EBUSY. */ + unsigned int sleep_microseconds = 10000; + unsigned int max_sleep_seconds = 1; + unsigned int n_sleep = (max_sleep_seconds + * 1000000 / sleep_microseconds); + int i; + for (i = 0; i < n_sleep; i++) { + if (i) + usleep (sleep_microseconds); + bool busy = false; + int j; + for (j = 0; j < lpn; j++) { + if (!ok[j]) { + ok[j] = _blkpg_remove_partition (disk, j + 1); + errnums[j] = errno; + if (!ok[j] && errnums[j] == EBUSY) + busy = true; + } + } + if (!busy) + break; + } + + for (i = 1; i <= lpn; i++) { + const PedPartition *part = ped_disk_get_partition (disk, i); + if (part) { + if (!ok[i - 1] && errnums[i - 1] == EBUSY) { + struct hd_geometry geom; + unsigned long long length = 0; + /* get start and length of existing partition */ + char *dev_name = _device_get_part_path (disk->dev, i); + if (!dev_name) + goto cleanup; + int fd = open (dev_name, O_RDONLY); + if (fd == -1 + || ioctl (fd, HDIO_GETGEO, &geom) + || ioctl (fd, BLKGETSIZE64, &length)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unable to determine the size and length of %s."), + dev_name); + if (fd != -1) + close (fd); + free (dev_name); + goto cleanup; + } + free (dev_name); + length /= disk->dev->sector_size; + close (fd); + if (geom.start == part->geom.start + && length == part->geom.length) + ok[i - 1] = 1; + /* If the new partition is unchanged and the + existing one was not removed because it was + in use, then reset the error flag and do not + try to add it since it is already there. */ + continue; + } + + /* add the (possibly modified or new) partition */ + if (!_blkpg_add_partition (disk, part)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Failed to add partition %d (%s)"), + i, strerror (errno)); + goto cleanup; + } + } + } + + char *bad_part_list = NULL; + /* now warn about any errors */ + for (i = 1; i <= lpn; i++) { + if (ok[i - 1] || errnums[i - 1] == ENXIO) + continue; + if (bad_part_list == NULL) { + bad_part_list = malloc (lpn * 5); + if (!bad_part_list) + goto cleanup; + bad_part_list[0] = 0; + } + sprintf (bad_part_list + strlen (bad_part_list), "%d, ", i); + } + if (bad_part_list == NULL) + ret = 1; + else { + bad_part_list[strlen (bad_part_list) - 2] = 0; + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition(s) %s on %s have been written, but we have " + "been unable to inform the kernel of the change, " + "probably because it/they are in use. As a result, " + "the old partition(s) will remain in use. You " + "should reboot now before making further changes."), + bad_part_list, disk->dev->path) == PED_EXCEPTION_IGNORE) + ret = 1; + free (bad_part_list); + } + cleanup: + free (errnums); + free (ok); + return ret; +} + +static int +_have_blkpg () +{ + static int have_blkpg = -1; + int kver; + + if (have_blkpg != -1) + return have_blkpg; + + kver = _get_linux_version(); + return have_blkpg = kver >= KERNEL_VERSION (2,4,0) ? 1 : 0; +} + +/* Return nonzero upon success, 0 if something fails. */ +static int +linux_disk_commit (PedDisk* disk) +{ + if (!_has_partitions (disk)) + return 1; + + if (disk->dev->type != PED_DEVICE_FILE) { + + /* We now require BLKPG support. If this assertion fails, + please write to the mailing list describing your system. + Assuming it's never triggered, ... + FIXME: remove this assertion in 2012. */ + assert (_have_blkpg ()); + + if (!_disk_sync_part_table (disk)) + return 0; + } + + return 1; +} + +#if USE_BLKID +static PedAlignment* +linux_get_minimum_alignment(const PedDevice *dev) +{ + blkid_topology tp = LINUX_SPECIFIC(dev)->topology; + if (!tp) + return NULL; + + if (blkid_topology_get_minimum_io_size(tp) == 0) + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / + dev->sector_size, + dev->phys_sector_size / dev->sector_size); + + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / dev->sector_size, + blkid_topology_get_minimum_io_size(tp) / dev->sector_size); +} + +static PedAlignment* +linux_get_optimum_alignment(const PedDevice *dev) +{ + blkid_topology tp = LINUX_SPECIFIC(dev)->topology; + if (!tp) + return NULL; + + /* When PED_DEFAULT_ALIGNMENT is divisible by the *_io_size or + there are no *_io_size values, use the PED_DEFAULT_ALIGNMENT + If one or the other will not divide evenly, fall through to + previous logic. */ + unsigned long optimal_io = blkid_topology_get_optimal_io_size(tp); + unsigned long minimum_io = blkid_topology_get_minimum_io_size(tp); + if ( + (!optimal_io && !minimum_io) + || (optimal_io && PED_DEFAULT_ALIGNMENT % optimal_io == 0 + && minimum_io && PED_DEFAULT_ALIGNMENT % minimum_io == 0) + || (!minimum_io && optimal_io + && PED_DEFAULT_ALIGNMENT % optimal_io == 0) + || (!optimal_io && minimum_io + && PED_DEFAULT_ALIGNMENT % minimum_io == 0) + ) { + /* DASD needs to use minimum alignment */ + if (dev->type == PED_DEVICE_DASD) + return linux_get_minimum_alignment(dev); + + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / dev->sector_size, + PED_DEFAULT_ALIGNMENT / dev->sector_size); + } + + /* If optimal_io_size is 0 and we don't meet the other criteria + for using the device.c default, return the minimum alignment. */ + if (blkid_topology_get_optimal_io_size(tp) == 0) + return linux_get_minimum_alignment(dev); + + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / dev->sector_size, + blkid_topology_get_optimal_io_size(tp) / dev->sector_size); +} +#endif + +static PedDeviceArchOps linux_dev_ops = { + _new: linux_new, + destroy: linux_destroy, + is_busy: linux_is_busy, + open: linux_open, + refresh_open: linux_refresh_open, + close: linux_close, + refresh_close: linux_refresh_close, + read: linux_read, + write: linux_write, + check: linux_check, + sync: linux_sync, + sync_fast: linux_sync_fast, + probe_all: linux_probe_all, +#if USE_BLKID + get_minimum_alignment: linux_get_minimum_alignment, + get_optimum_alignment: linux_get_optimum_alignment, +#endif +}; + +PedDiskArchOps linux_disk_ops = { + partition_get_path: linux_partition_get_path, + partition_is_busy: linux_partition_is_busy, + disk_commit: linux_disk_commit +}; + +PedArchitecture ped_linux_arch = { + dev_ops: &linux_dev_ops, + disk_ops: &linux_disk_ops +}; diff --git a/libparted/linux.h b/libparted/linux.h new file mode 100644 index 0000000..ca57361 --- /dev/null +++ b/libparted/linux.h @@ -0,0 +1,44 @@ +/* libparted - a library for manipulating disk partitions + Copyright (C) 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PED_ARCH_LINUX_H_INCLUDED +#define PED_ARCH_LINUX_H_INCLUDED + +#if HAVE_BLKID_BLKID_H +# include <blkid/blkid.h> +#endif + +#define LINUX_SPECIFIC(dev) ((LinuxSpecific*) (dev)->arch_specific) + +typedef struct _LinuxSpecific LinuxSpecific; + +struct _LinuxSpecific { + int fd; + int major; + int minor; + char* dmtype; /**< device map target type */ +#if defined __s390__ || defined __s390x__ + unsigned int real_sector_size; + unsigned int devno; +#endif +#if USE_BLKID + blkid_probe probe; + blkid_topology topology; +#endif +}; + +#endif /* PED_ARCH_LINUX_H_INCLUDED */ diff --git a/libparted/timer.c b/libparted/timer.c new file mode 100644 index 0000000..d12b801 --- /dev/null +++ b/libparted/timer.c @@ -0,0 +1,243 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file timer.c */ + +/** + * \addtogroup PedTimer + * + * \brief A PedTimer keeps track of the progress of a single (possibly + * compound) operation. + * + * The user of libparted constructs a PedTimer, and passes it to libparted + * functions that are likely to be expensive operations + * (like ped_file_system_resize). Use of timers is optional... you may + * pass NULL instead. + * + * When you create a PedTimer, you must specify a timer handler function. + * This will be called when there's an update on how work is progressing. + * + * Timers may be nested. When a timer is constructed, you can choose + * to assign it a parent, along with an estimate of what proportion of + * the total (parent's) time will be used in the nested operation. In + * this case, the nested timer's handler is internal to libparted, + * and simply updates the parent's progress, and calls its handler. + * + * @{ + */ + + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> + +typedef struct { + PedTimer* parent; + float nest_frac; + float start_frac; +} NestedContext; + + +/** + * \brief Creates a timer. + * + * Context will be passed in the \p context + * argument to the \p handler, when it is invoked. + * + * \return a new PedTimer + */ +PedTimer* +ped_timer_new (PedTimerHandler* handler, void* context) +{ + PedTimer* timer; + + PED_ASSERT (handler != NULL); + + timer = (PedTimer*) ped_malloc (sizeof (PedTimer)); + if (!timer) + return NULL; + + timer->handler = handler; + timer->context = context; + ped_timer_reset (timer); + return timer; +} + + +/** + * \brief Destroys a \p timer. + */ +void +ped_timer_destroy (PedTimer* timer) +{ + if (!timer) + return; + + free (timer); +} + +/* This function is used by ped_timer_new_nested() as the timer->handler + * function. + */ +static void +_nest_handler (PedTimer* timer, void* context) +{ + NestedContext* ncontext = (NestedContext*) context; + + ped_timer_update ( + ncontext->parent, + ncontext->start_frac + ncontext->nest_frac * timer->frac); +} + + +/** + * \brief Creates a new nested timer. + * + * This function creates a "nested" timer that describes the progress + * of a subtask. \p parent is the parent timer, and \p nested_frac is + * the estimated proportion (between 0 and 1) of the time that will be + * spent doing the nested timer's operation. The timer should only be + * constructed immediately prior to starting the nested operation. + * (It will be inaccurate, otherwise). + * Updates to the progress of the subtask are propagated + * back through to the parent task's timer. + * + * \return nested timer + */ +PedTimer* +ped_timer_new_nested (PedTimer* parent, float nest_frac) +{ + NestedContext* context; + + if (!parent) + return NULL; + + PED_ASSERT (nest_frac >= 0.0); + PED_ASSERT (nest_frac <= 1.0); + + context = (NestedContext*) ped_malloc (sizeof (NestedContext)); + if (!context) + return NULL; + context->parent = parent; + context->nest_frac = nest_frac; + context->start_frac = parent->frac; + + return ped_timer_new (_nest_handler, context); +} + +/** + * \brief Destroys a nested \p timer. + */ +void +ped_timer_destroy_nested (PedTimer* timer) +{ + if (!timer) + return; + + free (timer->context); + ped_timer_destroy (timer); +} + +/** + * \internal + * + * \brief This function calls the update handler, making sure that it has + * the latest time. + * + * First it updates \p timer->now and recomputes \p timer->predicted_end, + * and then calls the handler. + */ +void +ped_timer_touch (PedTimer* timer) +{ + if (!timer) + return; + + timer->now = time (NULL); + if (timer->now > timer->predicted_end) + timer->predicted_end = timer->now; + + timer->handler (timer, timer->context); +} + +/** + * \internal + * + * \brief This function sets the \p timer into a "start of task" position. + * + * It resets the \p timer, by setting \p timer->start and \p timer->now + * to the current time. + */ +void +ped_timer_reset (PedTimer* timer) +{ + if (!timer) + return; + + timer->start = timer->now = timer->predicted_end = time (NULL); + timer->state_name = NULL; + timer->frac = 0; + + ped_timer_touch (timer); +} + +/** + * \internal + * + * \brief This function tells a \p timer what fraction \p frac of the task + * has been completed. + * + * Sets the new \p timer->frac, and calls ped_timer_touch(). + */ +void +ped_timer_update (PedTimer* timer, float frac) +{ + if (!timer) + return; + + timer->now = time (NULL); + timer->frac = frac; + + if (frac) + timer->predicted_end + = timer->start + + (long) ((timer->now - timer->start) / frac); + + ped_timer_touch (timer); +} + +/** + * \internal + * + * \brief This function changes the description of the current task that the + * \p timer describes. + * + * Sets a new name - \p state_name - for the current "phase" of the operation, + * and calls ped_timer_touch(). + */ +void +ped_timer_set_state_name (PedTimer* timer, const char* state_name) +{ + if (!timer) + return; + + timer->state_name = state_name; + ped_timer_touch (timer); +} + +/** @} */ diff --git a/libparted/unit.c b/libparted/unit.c new file mode 100644 index 0000000..dc4205b --- /dev/null +++ b/libparted/unit.c @@ -0,0 +1,577 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2005, 2007, 2009-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** \file unit.c */ + +/** + * \addtogroup PedUnit + * + * \brief The PedUnit module provides a standard mechanism for describing + * and parsing locations within devices in human-friendly plain text. + * + * Internally, libparted uses PedSector (which is typedef'ed to be long long + * in <parted/device.h>) to describe device locations such as the start and + * end of partitions. However, sector numbers are often long and unintuitive. + * For example, my extended partition starts at sector 208845. PedUnit allows + * this location to be represented in more intutitive ways, including "106Mb", + * "0Gb" and "0%", as well as "208845s". PedUnit aims to provide facilities + * to provide a consistent system for describing device locations all + * throughout libparted. + * + * PedUnit provides two basic services: converting a PedSector into a text + * representation, and parsing a text representation into a PedSector. + * PedUnit currently supports these units: + * + * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact, + * cylinder and percent. + * + * PedUnit has a global variable that contains the default unit for all + * conversions. + * + * @{ + */ + + + + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> + +#include <ctype.h> +#include <stdio.h> +#include <float.h> + +#define N_(String) String +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + + +static PedUnit default_unit = PED_UNIT_COMPACT; +static const char* unit_names[] = { + "s", + "B", + "kB", + "MB", + "GB", + "TB", + "compact", + "cyl", + "chs", + "%", + "kiB", + "MiB", + "GiB", + "TiB" +}; + + +/** + * \brief Set the default \p unit used by subsequent calls to the PedUnit API. + * + * In particular, this affects how locations inside error messages + * (exceptions) are displayed. + */ +void +ped_unit_set_default (PedUnit unit) +{ + default_unit = unit; +} + + +/** + * \brief Get the current default unit. + */ +PedUnit +ped_unit_get_default () +{ + return default_unit; +} + +/** + * Get the byte size of a given \p unit. + */ +long long +ped_unit_get_size (const PedDevice* dev, PedUnit unit) +{ + PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; + + switch (unit) { + case PED_UNIT_SECTOR: return dev->sector_size; + case PED_UNIT_BYTE: return 1; + case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE; + case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE; + case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE; + case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE; + case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE; + case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE; + case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE; + case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE; + case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size; + case PED_UNIT_CHS: return dev->sector_size; + + case PED_UNIT_PERCENT: + return dev->length * dev->sector_size / 100; + + case PED_UNIT_COMPACT: + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Cannot get unit size for special unit " + "'COMPACT'.")); + return 0; + } + + /* never reached */ + PED_ASSERT(0); + return 0; +} + +/** + * Get a textual (non-internationalized) representation of a \p unit. + * + * For example, the textual representation of PED_UNIT_SECTOR is "s". + */ +const char* +ped_unit_get_name (PedUnit unit) +{ + return unit_names[unit]; +} + +/** + * Get a unit based on its textual representation: \p unit_name. + * + * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE. + */ +PedUnit +ped_unit_get_by_name (const char* unit_name) +{ + PedUnit unit; + for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) { + if (!strcasecmp (unit_names[unit], unit_name)) + return unit; + } + return -1; +} + +static char* +ped_strdup (const char *str) +{ + char *result; + result = ped_malloc (strlen (str) + 1); + if (!result) + return NULL; + strcpy (result, str); + return result; +} + +/** + * \brief Get a string that describes the location of the \p byte on + * device \p dev. + * + * The string is described with the desired \p unit. + * The returned string must be freed with free(). + */ +char* +ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit) +{ + char buf[100]; + PedSector sector = byte / dev->sector_size; + double d, w; + int p; + + PED_ASSERT (dev != NULL); + + /* CHS has a special comma-separated format. */ + if (unit == PED_UNIT_CHS) { + const PedCHSGeometry *chs = &dev->bios_geom; + snprintf (buf, 100, "%lld,%lld,%lld", + sector / chs->sectors / chs->heads, + (sector / chs->sectors) % chs->heads, + sector % chs->sectors); + return ped_strdup (buf); + } + + /* Cylinders, sectors and bytes should be rounded down... */ + if (unit == PED_UNIT_CYLINDER + || unit == PED_UNIT_SECTOR + || unit == PED_UNIT_BYTE) { + snprintf (buf, 100, "%lld%s", + byte / ped_unit_get_size (dev, unit), + ped_unit_get_name (unit)); + return ped_strdup (buf); + } + + if (unit == PED_UNIT_COMPACT) { + if (byte >= 10LL * PED_TERABYTE_SIZE) + unit = PED_UNIT_TERABYTE; + else if (byte >= 10LL * PED_GIGABYTE_SIZE) + unit = PED_UNIT_GIGABYTE; + else if (byte >= 10LL * PED_MEGABYTE_SIZE) + unit = PED_UNIT_MEGABYTE; + else if (byte >= 10LL * PED_KILOBYTE_SIZE) + unit = PED_UNIT_KILOBYTE; + else + unit = PED_UNIT_BYTE; + } + + /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */ + /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */ + /* This just divide by 2 the natural IEEE754 extended precision */ + /* and won't cause any trouble before 1000 TB */ + d = ((double)byte / ped_unit_get_size (dev, unit)) + * (1. + DBL_EPSILON); + w = d + ( (d < 10. ) ? 0.005 : + (d < 100.) ? 0.05 : + 0.5 ); + p = (w < 10. ) ? 2 : + (w < 100.) ? 1 : + 0 ; + +#ifdef __BEOS__ + snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit)); +#else + snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit)); +#endif + + return ped_strdup (buf); +} + +/** + * \brief Get a string that describes the location of the \p byte on + * device \p dev. + * + * The string is described with the default unit, which is set + * by ped_unit_set_default(). + * The returned string must be freed with free(). + */ +char* +ped_unit_format_byte (const PedDevice* dev, PedSector byte) +{ + PED_ASSERT (dev != NULL); + return ped_unit_format_custom_byte (dev, byte, default_unit); +} + +/** + * \brief Get a string that describes the location \p sector on device \p dev. + * + * The string is described with the desired \p unit. + * The returned string must be freed with free(). + */ +char* +ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit) +{ + PED_ASSERT (dev != NULL); + return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit); +} + +/** + * \brief Get a string that describes the location \p sector on device \p dev. + * + * The string is described with the default unit, which is set + * by ped_unit_set_default(). + * The returned string must be freed with free(). + */ +char* +ped_unit_format (const PedDevice* dev, PedSector sector) +{ + PED_ASSERT (dev != NULL); + return ped_unit_format_custom_byte (dev, sector * dev->sector_size, + default_unit); +} + +/** + * If \p str contains a valid description of a location on \p dev, + * then \p *sector is modified to describe the location and a geometry + * is created in \p *range describing a 2 units large area centered on + * \p *sector. If the \p range as described here would be partially outside + * the device \p dev, the geometry returned is the intersection between the + * former and the whole device geometry. If no units are specified, then the + * default unit is assumed. + * + * \return \c 1 if \p str is a valid location description, \c 0 otherwise + */ +int +ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector, + PedGeometry** range) +{ + return ped_unit_parse_custom (str, dev, default_unit, sector, range); +} + +/* Inefficiently removes all spaces from a string, in-place. */ +static void +strip_string (char* str) +{ + int i; + + for (i = 0; str[i] != 0; i++) { + if (isspace (str[i])) { + int j; + for (j = i + 1; str[j] != 0; j++) + str[j - 1] = str[j]; + } + } +} + + +/* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to + * "Mb". */ +static char* +find_suffix (const char* str) +{ + while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0]))) + str++; + return (char *) str; +} + +static void +remove_punct (char* str) +{ + int i = 0; + + for (i = 0; str[i]; i++) { + if (ispunct (str[i])) + str[i] = ' '; + } +} + +static int +is_chs (const char* str) +{ + int punct_count = 0; + int i = 0; + + for (i = 0; str[i]; i++) + punct_count += ispunct (str[i]) != 0; + return punct_count == 2; +} + +static int +parse_chs (const char* str, const PedDevice* dev, PedSector* sector, + PedGeometry** range) +{ + PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; + PedCHSGeometry chs; + + char* copy = ped_strdup (str); + if (!copy) + return 0; + strip_string (copy); + remove_punct (copy); + + if (sscanf (copy, "%d %d %d", + &chs.cylinders, &chs.heads, &chs.sectors) != 3) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("\"%s\" has invalid syntax for locations."), + copy); + goto error_free_copy; + } + + if (chs.heads >= dev->bios_geom.heads) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The maximum head value is %d."), + dev->bios_geom.heads - 1); + goto error_free_copy; + } + if (chs.sectors >= dev->bios_geom.sectors) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The maximum sector value is %d."), + dev->bios_geom.sectors - 1); + goto error_free_copy; + } + + *sector = 1LL * chs.cylinders * cyl_size + + chs.heads * dev->bios_geom.sectors + + chs.sectors; + + if (*sector >= dev->length) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The location %s is outside of the " + "device %s."), + str, dev->path); + goto error_free_copy; + } + if (range) + *range = ped_geometry_new (dev, *sector, 1); + free (copy); + return !range || *range != NULL; + +error_free_copy: + free (copy); + *sector = 0; + if (range) + *range = NULL; + return 0; +} + +static PedSector +clip (const PedDevice* dev, PedSector sector) +{ + if (sector < 0) + return 0; + if (sector > dev->length - 1) + return dev->length - 1; + return sector; +} + +static PedGeometry* +geometry_from_centre_radius (const PedDevice* dev, + PedSector sector, PedSector radius) +{ + PedSector start = clip (dev, sector - radius); + PedSector end = clip (dev, sector + radius); + if (sector - end > radius || start - sector > radius) + return NULL; + return ped_geometry_new (dev, start, end - start + 1); +} + +static PedUnit +parse_unit_suffix (const char* suffix, PedUnit suggested_unit) +{ + if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') { + switch (tolower (suffix[0])) { + case 'k': return PED_UNIT_KIBIBYTE; + case 'm': return PED_UNIT_MEBIBYTE; + case 'g': return PED_UNIT_GIBIBYTE; + case 't': return PED_UNIT_TEBIBYTE; + } + } else if (strlen (suffix) > 0) { + switch (tolower (suffix[0])) { + case 's': return PED_UNIT_SECTOR; + case 'b': return PED_UNIT_BYTE; + case 'k': return PED_UNIT_KILOBYTE; + case 'm': return PED_UNIT_MEGABYTE; + case 'g': return PED_UNIT_GIGABYTE; + case 't': return PED_UNIT_TERABYTE; + case 'c': return PED_UNIT_CYLINDER; + case '%': return PED_UNIT_PERCENT; + } + } + + if (suggested_unit == PED_UNIT_COMPACT) { + if (default_unit == PED_UNIT_COMPACT) + return PED_UNIT_MEGABYTE; + else + return default_unit; + } + + return suggested_unit; +} + +static bool +is_power_of_2 (long long n) +{ + return (n & (n - 1)) == 0; +} + +/** + * If \p str contains a valid description of a location on \p dev, then + * \p *sector is modified to describe the location and a geometry is created + * in \p *range describing a 2 units large area centered on \p *sector. If the + * \p range as described here would be partially outside the device \p dev, the + * geometry returned is the intersection between the former and the whole + * device geometry. If no units are specified, then the default unit is + * assumed. + * + * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a + * location + * \throws PED_EXCEPTION_ERROR if location described by \p str + * is outside of the device \p dev->path + * + * \return \c 1 if \p str is a valid location description, \c 0 otherwise. + */ +int +ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit, + PedSector* sector, PedGeometry** range) +{ + char* copy; + char* suffix; + double num; + long long unit_size; + PedSector radius; + + if (is_chs (str)) + return parse_chs (str, dev, sector, range); + + copy = ped_strdup (str); + if (!copy) + goto error; + strip_string (copy); + + suffix = find_suffix (copy); + unit = parse_unit_suffix (suffix, unit); + suffix[0] = 0; + + if (sscanf (copy, "%lf", &num) != 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid number.")); + goto error_free_copy; + } + + unit_size = ped_unit_get_size (dev, unit); + radius = ped_div_round_up (unit_size, dev->sector_size) - 1; + if (radius < 0) + radius = 0; + /* If the user specifies units in a power of 2, e.g., 4MiB, as in + parted -s -- $dev mklabel gpt mkpart P-NAME 4MiB -34s + do not use 4MiB as the range. Rather, presume that they + are specifying precisely the starting or ending number, + and treat "4MiB" just as we would treat "4194304B". */ + if (is_power_of_2 (unit_size)) + radius = 0; + + *sector = num * unit_size / dev->sector_size; + /* negative numbers count from the end */ + if (copy[0] == '-') + *sector += dev->length; + if (range) { + *range = geometry_from_centre_radius (dev, *sector, radius); + if (!*range) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The location %s is outside of the " + "device %s."), + str, dev->path); + goto error_free_copy; + } + } + *sector = clip (dev, *sector); + + free (copy); + return 1; + +error_free_copy: + free (copy); +error: + *sector = 0; + if (range) + *range = NULL; + return 0; +} + + +/** @} */ |