aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS236
-rw-r--r--COPYING675
-rw-r--r--README18
-rw-r--r--cmd/.gitignore1
-rw-r--r--cmd/Makefile17
-rw-r--r--cmd/cmd.c80
-rw-r--r--gnulib/argmatch.c277
-rw-r--r--gnulib/basename-lgpl.c75
-rw-r--r--gnulib/close-stream.c74
-rw-r--r--gnulib/close-stream.h2
-rw-r--r--gnulib/closeout.c124
-rw-r--r--gnulib/exitfail.c24
-rw-r--r--gnulib/exitfail.h18
-rw-r--r--gnulib/gettext.h286
-rw-r--r--gnulib/intprops.h320
-rw-r--r--gnulib/progname.c92
-rw-r--r--gnulib/quote.c40
-rw-r--r--gnulib/quote.h20
-rw-r--r--gnulib/quotearg.c888
-rw-r--r--gnulib/quotearg.h389
-rw-r--r--gnulib/version-etc-fsf.c30
-rw-r--r--gnulib/version-etc.c258
-rw-r--r--gnulib/xalloc-die.c41
-rw-r--r--gnulib/xmalloc.c124
-rw-r--r--gnulib/xstrtol.c228
-rw-r--r--gnulib/xstrtoll.c6
-rw-r--r--include/argmatch.h102
-rw-r--r--include/closeout.h36
-rw-r--r--include/config.h1572
-rw-r--r--include/configmake.h27
-rw-r--r--include/dirname.h46
-rw-r--r--include/dosname.h53
-rw-r--r--include/parted/constraint.h96
-rw-r--r--include/parted/debug.h88
-rw-r--r--include/parted/device.h165
-rw-r--r--include/parted/disk.h389
-rw-r--r--include/parted/endian.h84
-rw-r--r--include/parted/exception.h115
-rw-r--r--include/parted/filesys.h135
-rw-r--r--include/parted/geom.h85
-rw-r--r--include/parted/natmath.h108
-rw-r--r--include/parted/parted.h51
-rw-r--r--include/parted/timer.h65
-rw-r--r--include/parted/unit.h91
-rw-r--r--include/progname.h62
-rw-r--r--include/verify.h232
-rw-r--r--include/version-etc.h78
-rw-r--r--include/version.h0
-rw-r--r--include/xalloc-oversized.h38
-rw-r--r--include/xalloc.h277
-rw-r--r--include/xstrtol.h79
-rw-r--r--libparted/Makefile21
-rw-r--r--libparted/architecture.c43
-rw-r--r--libparted/architecture.h38
-rw-r--r--libparted/cs/constraint.c527
-rw-r--r--libparted/cs/geom.c492
-rw-r--r--libparted/cs/natmath.c481
-rw-r--r--libparted/debug.c114
-rw-r--r--libparted/device.c565
-rw-r--r--libparted/disk.c2444
-rw-r--r--libparted/exception.c311
-rw-r--r--libparted/filesys.c861
-rw-r--r--libparted/fs/fat/Makefile9
-rw-r--r--libparted/fs/fat/bootsector.c452
-rw-r--r--libparted/fs/fat/bootsector.h131
-rw-r--r--libparted/fs/fat/calc.c433
-rw-r--r--libparted/fs/fat/calc.h76
-rw-r--r--libparted/fs/fat/clstdup.c422
-rw-r--r--libparted/fs/fat/clstdup.h27
-rw-r--r--libparted/fs/fat/context.c260
-rw-r--r--libparted/fs/fat/context.h69
-rw-r--r--libparted/fs/fat/count.c401
-rw-r--r--libparted/fs/fat/count.h45
-rw-r--r--libparted/fs/fat/fat.c888
-rw-r--r--libparted/fs/fat/fat.h158
-rw-r--r--libparted/fs/fat/fatio.c149
-rw-r--r--libparted/fs/fat/fatio.h48
-rw-r--r--libparted/fs/fat/resize.c877
-rw-r--r--libparted/fs/fat/table.c480
-rw-r--r--libparted/fs/fat/table.h73
-rw-r--r--libparted/fs/fat/traverse.c367
-rw-r--r--libparted/fs/fat/traverse.h74
-rw-r--r--libparted/fs/hfs/DOC92
-rw-r--r--libparted/fs/hfs/HISTORY115
-rw-r--r--libparted/fs/hfs/Makefile10
-rw-r--r--libparted/fs/hfs/TODO27
-rw-r--r--libparted/fs/hfs/advfs.c328
-rw-r--r--libparted/fs/hfs/advfs.h48
-rw-r--r--libparted/fs/hfs/advfs_plus.c382
-rw-r--r--libparted/fs/hfs/advfs_plus.h51
-rw-r--r--libparted/fs/hfs/cache.c238
-rw-r--r--libparted/fs/hfs/cache.h117
-rw-r--r--libparted/fs/hfs/file.c228
-rw-r--r--libparted/fs/hfs/file.h41
-rw-r--r--libparted/fs/hfs/file_plus.c273
-rw-r--r--libparted/fs/hfs/file_plus.h60
-rw-r--r--libparted/fs/hfs/hfs.c1354
-rw-r--r--libparted/fs/hfs/hfs.h647
-rw-r--r--libparted/fs/hfs/journal.c389
-rw-r--r--libparted/fs/hfs/journal.h44
-rw-r--r--libparted/fs/hfs/probe.c230
-rw-r--r--libparted/fs/hfs/probe.h43
-rw-r--r--libparted/fs/hfs/reloc.c669
-rw-r--r--libparted/fs/hfs/reloc.h35
-rw-r--r--libparted/fs/hfs/reloc_plus.c942
-rw-r--r--libparted/fs/hfs/reloc_plus.h36
-rw-r--r--libparted/libparted.abin0 -> 302519 bytes
-rw-r--r--libparted/libparted.c165
-rw-r--r--libparted/linux.c2616
-rw-r--r--libparted/linux.h44
-rw-r--r--libparted/timer.c243
-rw-r--r--libparted/unit.c577
112 files changed, 29797 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..530a28b
--- /dev/null
+++ b/AUTHORS
@@ -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
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..24afbe2
--- /dev/null
+++ b/COPYING
@@ -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>.
diff --git a/README b/README
new file mode 100644
index 0000000..76f4e4d
--- /dev/null
+++ b/README
@@ -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
new file mode 100644
index 0000000..4654f3f
--- /dev/null
+++ b/libparted/libparted.a
Binary files differ
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, &sector_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;
+}
+
+
+/** @} */