aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2011-08-10 14:50:09 +0200
committerChristoph Hellwig <hch@lst.de>2011-08-10 14:50:09 +0200
commit08a9c29640f51e41125e2274793469e16fc3832f (patch)
tree561c49bf892206ed6133f88c18a3c3484106e391
downloadfsresize-master.tar.gz
initial checkingHEADmaster
This is a simple filesystem resizing tool for HFS and fat. It is based on the old resize code that was dropped on parted. The import is based on revision 077ece22959f808df80d2ffa9f0bc5fdf4a0707a in the parted git repository.
-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;
+}
+
+
+/** @} */