summaryrefslogtreecommitdiff
path: root/fdisks
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-11-02 20:15:39 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-11-02 20:15:39 +0400
commitb13154de3eca5ba28fbb4854d916cd0be5febeed (patch)
tree30f2e9e89ab71a2df837076ac68c3ba770230294 /fdisks
downloadutil-linux-upstream.tar.gz
Imported Upstream version 2.22upstream/2.22upstream
Diffstat (limited to 'fdisks')
-rw-r--r--fdisks/Makemodule.am87
-rw-r--r--fdisks/cfdisk.8452
-rw-r--r--fdisks/cfdisk.c2854
-rw-r--r--fdisks/common.h15
-rw-r--r--fdisks/fdisk.8288
-rw-r--r--fdisks/fdisk.c1935
-rw-r--r--fdisks/fdisk.h294
-rw-r--r--fdisks/fdiskaixlabel.c90
-rw-r--r--fdisks/fdiskaixlabel.h25
-rw-r--r--fdisks/fdiskbsdlabel.c855
-rw-r--r--fdisks/fdiskbsdlabel.h245
-rw-r--r--fdisks/fdiskdoslabel.c833
-rw-r--r--fdisks/fdiskdoslabel.h47
-rw-r--r--fdisks/fdiskmaclabel.c108
-rw-r--r--fdisks/fdiskmaclabel.h34
-rw-r--r--fdisks/fdisksgilabel.c907
-rw-r--r--fdisks/fdisksgilabel.h131
-rw-r--r--fdisks/fdisksunlabel.c661
-rw-r--r--fdisks/fdisksunlabel.h91
-rw-r--r--fdisks/gpt.c216
-rw-r--r--fdisks/gpt.h7
-rw-r--r--fdisks/i386_sys_types.c110
-rw-r--r--fdisks/partname.c48
-rw-r--r--fdisks/sfdisk.8603
-rw-r--r--fdisks/sfdisk.c3242
-rw-r--r--fdisks/utils.c523
26 files changed, 14701 insertions, 0 deletions
diff --git a/fdisks/Makemodule.am b/fdisks/Makemodule.am
new file mode 100644
index 0000000..f586769
--- /dev/null
+++ b/fdisks/Makemodule.am
@@ -0,0 +1,87 @@
+
+fdisk_common_sources = \
+ fdisks/common.h \
+ fdisks/gpt.c \
+ fdisks/gpt.h \
+ fdisks/i386_sys_types.c
+
+if !ARCH_M68K
+
+sbin_PROGRAMS += fdisk
+dist_man_MANS += fdisks/fdisk.8
+fdisk_SOURCES = \
+ fdisks/utils.c \
+ fdisks/fdisk.c \
+ fdisks/fdisk.h \
+ fdisks/fdiskaixlabel.c \
+ fdisks/fdiskaixlabel.h \
+ fdisks/fdiskbsdlabel.c \
+ fdisks/fdiskbsdlabel.h \
+ fdisks/fdiskmaclabel.c \
+ fdisks/fdiskmaclabel.h \
+ fdisks/fdisksgilabel.c \
+ fdisks/fdisksgilabel.h \
+ fdisks/fdisksunlabel.c \
+ fdisks/fdisksunlabel.h \
+ fdisks/fdiskdoslabel.c \
+ fdisks/fdiskdoslabel.h \
+ fdisks/partname.c \
+ $(fdisk_common_sources)
+
+fdisk_LDADD = $(LDADD) libcommon.la
+
+if BUILD_LIBBLKID
+fdisk_CFLAGS = -I$(ul_libblkid_incdir)
+fdisk_LDADD += libblkid.la
+endif
+
+if HAVE_STATIC_FDISK
+sbin_PROGRAMS += fdisk.static
+fdisk_static_SOURCES = $(fdisk_SOURCES)
+fdisk_static_LDFLAGS = -all-static
+fdisk_static_CFLAGS = $(fdisk_CFLAGS)
+fdisk_static_LDADD = $(fdisk_LDADD)
+endif
+
+
+if !ARCH_SPARC
+
+sbin_PROGRAMS += sfdisk
+dist_man_MANS += fdisks/sfdisk.8
+sfdisk_SOURCES = \
+ fdisks/partname.c \
+ fdisks/sfdisk.c \
+ $(fdisk_common_sources)
+sfdisk_LDADD = $(LDADD) libcommon.la
+
+if HAVE_STATIC_SFDISK
+sbin_PROGRAMS += sfdisk.static
+sfdisk_static_SOURCES = $(sfdisk_SOURCES)
+sfdisk_static_LDFLAGS = -all-static
+sfdisk_static_LDADD = $(sfdisk_LDADD)
+endif
+
+
+if BUILD_CFDISK
+sbin_PROGRAMS += cfdisk
+dist_man_MANS += fdisks/cfdisk.8
+cfdisk_SOURCES = fdisks/cfdisk.c $(fdisk_common_sources)
+cfdisk_CFLAGS =
+cfdisk_LDADD = $(LDADD) libcommon.la
+
+if BUILD_LIBBLKID
+cfdisk_CFLAGS += -I$(ul_libblkid_incdir)
+cfdisk_LDADD += libblkid.la
+endif
+
+if HAVE_SLANG
+cfdisk_LDADD += -lslang
+else
+if HAVE_NCURSES
+cfdisk_LDADD += @NCURSES_LIBS@
+endif
+endif
+endif # BUILD_CFDISK
+
+endif # !ARCH_SPARC
+endif # !ARCH_M68K
diff --git a/fdisks/cfdisk.8 b/fdisks/cfdisk.8
new file mode 100644
index 0000000..28ddad6
--- /dev/null
+++ b/fdisks/cfdisk.8
@@ -0,0 +1,452 @@
+.\" cfdisk.8 -- man page for cfdisk
+.\" Copyright 1994 Kevin E. Martin (martin@cs.unc.edu)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" " for hilit mode
+.TH CFDISK 8 "July 2009" "util-linux" "System Administration"
+.SH NAME
+cfdisk \- display or manipulate disk partition table
+.SH SYNOPSIS
+.B cfdisk
+.RB [ \-agvz ]
+.RB [ \-c
+.IR cylinders ]
+.RB [ \-h
+.IR heads ]
+.RB [ \-s
+.IR sectors-per-track ]
+.RB [ \-P
+.IR opt ]
+.RI [ device ]
+.SH DESCRIPTION
+.B cfdisk
+is a curses-based program for partitioning any hard disk drive.
+Typical values of the
+.I device
+argument are:
+.sp
+.nf
+.RS
+/dev/hda [default]
+/dev/hdb
+/dev/sda
+/dev/sdb
+/dev/sdc
+/dev/sdd
+.RE
+.fi
+
+Note that
+.B cfdisk
+does not align partitions to block device I/O limits. This functionality is
+provided by
+.BR fdisk (8).
+
+In order to write the partition table
+.B cfdisk
+needs something called the `geometry' of the disk: the number
+of `heads' and the number of `sectors per track'. Linux does not
+use any geometry, so if the disk will not be accessed by other
+operating systems, you can safely accept the defaults that
+.B cfdisk
+chooses for you. The geometry used by
+.B cfdisk
+is found as follows. First the partition table is examined,
+to see what geometry was used by the previous program that
+changed it. If the partition table is empty, or contains garbage,
+or does not point at a consistent geometry, the kernel is
+asked for advice. If nothing works 255 heads and 63 sectors/track
+is assumed. The geometry can be overridden on the command line
+or by use of the `g' command. When partitioning an empty large modern
+disk, picking 255 heads and 63 sectors/track is always a good idea.
+There is no need to set the number of cylinders, since
+.B cfdisk
+knows the disk size.
+
+Next,
+.B cfdisk
+tries to read the current partition table from the disk drive. If it
+is unable to figure out the partition table, an error is displayed and
+the program will exit. This might also be caused by incorrect
+geometry information, and can be overridden on the command line.
+Another way around this problem is with the
+.B \-z
+option. This will ignore the partition table on the disk.
+
+The main display is composed of four sections, from top to bottom: the
+header, the partitions, the command line and a warning line. The
+header contains the program name and version number followed by the
+disk drive and its geometry. The partitions section always displays
+the current partition table. The command line is the place where
+commands and text are entered. The available commands are usually
+displayed in brackets. The warning line is usually empty except when
+there is important information to be displayed. The current partition
+is highlighted with reverse video (or an arrow if the
+.B \-a
+option is given). All partition specific commands apply to the
+current partition.
+
+The format of the partition table in the partitions section is, from
+left to right: Name, Flags, Partition Type, Filesystem Type and Size.
+The name is the partition device name. The flags can be
+.IR Boot ,
+which designates a bootable partition or
+.IR NC ,
+which stands for "Not Compatible with DOS or OS/2". DOS, OS/2 and
+possibly other operating systems require the first sector of the first
+partition on the disk and all logical partitions to begin on the
+second head. This wastes the second through the last sector of the
+first track of the first head (the first sector is taken by the
+partition table itself).
+.B cfdisk
+allows you to recover these "lost" sectors with the maximize command
+.RB ( m ).
+.I Note:
+.BR fdisk (8)
+and some early versions of DOS create all partitions with the number
+of sectors already maximized. For more information, see the maximize
+command below. The partition type can be one of
+.IR Primary " or " Logical .
+For unallocated space on the drive, the partition type can also be
+.IR Pri/Log ,
+or empty (if the space is unusable). The filesystem type section
+displays the name of the filesystem used on the partition, if known.
+If it is unknown, then
+.I Unknown
+and the hex value of the filesystem type are displayed. A special
+case occurs when there are sections of the disk drive that cannot be
+used (because all of the primary partitions are used). When this is
+detected, the filesystem type is displayed as
+.IR Unusable .
+The size field displays the size of the partition in megabytes (by
+default). It can also display the size in sectors and cylinders (see
+the change units command below). If an asterisk
+.RB ( * )
+appears after the size, this means that the partition is not aligned
+on cylinder boundaries.
+.SH "DOS 6.x WARNING"
+
+The DOS 6.x FORMAT command looks for some information in the first
+sector of the data area of the partition, and treats this information
+as more reliable than the information in the partition table. DOS
+FORMAT expects DOS FDISK to clear the first 512 bytes of the data area
+of a partition whenever a size change occurs. DOS FORMAT will look at
+this extra information even if the /U flag is given -- we consider
+this a bug in DOS FORMAT and DOS FDISK.
+
+The bottom line is that if you use cfdisk or fdisk to change the size of a
+DOS partition table entry, then you must also use
+.B dd
+to zero the first 512 bytes of that partition before using DOS FORMAT to
+format the partition. For example, if you were using cfdisk to make a DOS
+partition table entry for /dev/hda1, then (after exiting fdisk or cfdisk
+and rebooting Linux so that the partition table information is valid) you
+would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero
+the first 512 bytes of the partition. Note:
+
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+For best results, you should always use an OS-specific partition table
+program. For example, you should make DOS partitions with the DOS FDISK
+program and Linux partitions with the Linux fdisk or Linux cfdisk program.
+
+.SH COMMANDS
+.B cfdisk
+commands can be entered by pressing the desired key (pressing
+.I Enter
+after the command is not necessary). Here is a list of the available
+commands:
+.TP
+.B b
+Toggle bootable flag of the current partition. This allows you to
+select which primary partition is bootable on the drive.
+.TP
+.B d
+Delete the current partition. This will convert the current partition
+into free space and merge it with any free space immediately
+surrounding the current partition. A partition already marked as free
+space or marked as unusable cannot be deleted.
+.TP
+.B g
+Change the disk geometry (cylinders, heads, or sectors-per-track).
+.B WARNING:
+This option should only be used by people who know what they are
+doing. A command line option is also available to change the disk
+geometry. While at the change disk geometry command line, you can
+choose to change cylinders
+.RB ( c ),
+heads
+.RB ( h ),
+and sectors per track
+.RB ( s ).
+The default value will be printed at the prompt which you can accept
+by simply pressing the
+.I Enter
+key, or you can exit without changes by pressing the
+.I ESC
+key. If you want to change the default value, simply enter the
+desired value and press
+.IR Enter .
+The altered disk parameter values do not take effect until you return
+to the main menu (by pressing
+.IR Enter " or " ESC
+at the change disk geometry command line). If you change the geometry
+such that the disk appears larger, the extra sectors are added at the
+end of the disk as free space. If the disk appears smaller, the
+partitions that are beyond the new last sector are deleted and the
+last partition on the drive (or the free space at the end of the
+drive) is made to end at the new last sector.
+.TP
+.B h
+Print the help screen.
+.TP
+.B m
+Maximize disk usage of the current partition. This command will
+recover the unused space between the partition table and the
+beginning of the partition, but at the cost of making the partition
+incompatible with DOS, OS/2 and possibly other operating systems.
+This option will toggle between maximal disk usage and DOS, OS/2,
+etc. compatible disk usage. The default when creating a partition is
+to create DOS, OS/2, etc. compatible partitions.
+.TP
+.B n
+Create new partition from free space. If the partition type is
+.IR Primary " or " Logical ,
+a partition of that type will be created, but if the partition type is
+.IR Pri/Log ,
+you will be prompted for the type you want to create. Be aware that
+(1) there are only four slots available for primary partitions and (2)
+since there can be only one extended partition, which contains all of
+the logical drives, all of the logical drives must be contiguous (with
+no intervening primary partition).
+.B cfdisk
+next prompts you for the size of the partition you want to create.
+The default size, equal to the entire free space of the current
+partition, is displayed in megabytes. You can either press the
+.I Enter
+key to accept the default size or enter a different size at the
+prompt.
+.B cfdisk
+accepts size entries in megabytes
+.RB ( M )
+[default], kilobytes
+.RB ( K ),
+cylinders
+.RB ( C )
+and sectors
+.RB ( S )
+by entering the number immediately followed by one of
+.RB ( M ", " K ", " C " or " S ).
+If the partition fills the free space available, the partition is
+created and you are returned to the main command line. Otherwise, the
+partition can be created at the beginning or the end of the free
+space, and
+.B cfdisk
+will ask you to choose where to place the partition. After the
+partition is created,
+.B cfdisk
+automatically adjusts the other partitions' partition types if all of
+the primary partitions are used.
+.TP
+.B p
+Print the partition table to the screen or to a file. There are
+several different formats for the partition that you can choose from:
+.sp
+.RS
+.TP
+.B r
+Raw data format (exactly what would be written to disk)
+.TP
+.B s
+Partition table in sector order format
+.TP
+.B t
+Partition table in raw format
+.RE
+
+.RS
+The
+.I raw data format
+will print the sectors that would be written to disk if a
+.BR w rite
+command is selected. First, the primary partition table is printed,
+followed by the partition tables associated with each logical
+partition. The data is printed in hex byte by byte with 16 bytes per
+line.
+
+The
+.I partition table in sector order format
+will print the partition table ordered by sector number. The fields,
+from left to right, are the number of the partition, the partition
+type, the first sector, the last sector, the offset from the first
+sector of the partition to the start of the data, the length of the
+partition, the filesystem type (with the hex value in parenthesis),
+and the flags (with the hex value in parenthesis). In addition to the
+primary and logical partitions, free and unusable space is printed and
+the extended partition is printed before the first logical partition.
+
+If a partition does not start or end on a cylinder boundary or if the
+partition length is not divisible by the cylinder size, an asterisk
+.RB ( * )
+is printed after the non-aligned sector number/count. This usually
+indicates that a partition was created by an operating system that
+either does not align partitions to cylinder boundaries or that used
+different disk geometry information. If you know the disk geometry of
+the other operating system, you could enter the geometry information
+with the change geometry command
+.RB ( g ).
+
+For the first partition on the disk and for all logical partitions, if
+the offset from the beginning of the partition is not equal to the
+number of sectors per track (i.e., the data does not start on the
+first head), a number sign
+.RB ( # )
+is printed after the offset. For the remaining partitions, if the
+offset is not zero, a number sign will be printed after the offset.
+This corresponds to the
+.I NC
+flag in the partitions section of the main display.
+
+The
+.I partition table in raw format
+will print the partition table ordered by partition number. It will
+leave out all free and unusable space. The fields, from left to
+right, are the number of the partition, the flags (in hex), the
+starting head, sector and cylinder, the filesystem ID (in hex), the
+ending head, sector and cylinder, the starting sector in the partition
+and the number of sectors in the partition. The information in this
+table can be directly translated to the
+.IR "raw data format" .
+
+The partition table entries only have 10 bits available to represent
+the starting and ending cylinders. Thus, when the absolute starting
+(ending) sector number is on a cylinder greater than 1023, the maximal
+values for starting (ending) head, sector and cylinder are printed.
+This is the method used by OS/2, and thus fixes the problems
+associated with OS/2's fdisk rewriting the partition table when it is
+not in this format. Since Linux and OS/2 use absolute sector counts,
+the values in the starting and ending head, sector and cylinder are
+not used.
+.RE
+.TP
+.B q
+Quit program. This will exit the program without writing any data to
+disk.
+.TP
+.B t
+Change the filesystem type. By default, new partitions are created as
+.I Linux
+partitions, but since
+.B cfdisk
+can create partitions for other operating systems, change partition
+type allows you to enter the hex value of the filesystem you desire.
+A list of the know filesystem types is displayed. You can type in the
+filesystem type at the prompt or accept the default filesystem type
+.RI [ Linux ].
+.TP
+.B u
+Change units of the partition size display. It will rotate through
+megabytes, sectors and cylinders.
+.TP
+.B W
+Write partition table to disk (must enter an upper case W). Since
+this might destroy data on the disk, you must either confirm or deny
+the write by entering `yes' or `no'. If you enter `yes',
+.B cfdisk
+will write the partition table to disk and the tell the kernel to re-read the
+partition table from the disk. The re-reading of the partition table does not
+work in some cases, for example for device-mapper devices. In
+particular case you need to inform kernel about new
+partitions by
+.B partprobe(8),
+.B kpartx(8)
+or reboot the system.
+.TP
+.I Up Arrow
+.TP
+.I Down Arrow
+Move cursor to the previous or next partition. If there are more
+partitions than can be displayed on a screen, you can display the next
+(previous) set of partitions by moving down (up) at the last (first)
+partition displayed on the screen.
+.TP
+.I CTRL-L
+Redraws the screen. In case something goes wrong and you cannot read
+anything, you can refresh the screen from the main command line.
+.TP
+.B ?
+Print the help screen.
+
+.RE
+All of the commands can be entered with either upper or lower case
+letters (except for
+.BR W rites).
+When in a sub-menu or at a prompt to enter a filename, you can hit the
+.I ESC
+key to return to the main command line.
+.SH OPTIONS
+.TP
+.B \-a
+Use an arrow cursor instead of reverse video for highlighting the
+current partition.
+.TP
+.B \-g
+Do not use the geometry given by the disk driver, but try to
+guess a geometry from the partition table.
+.TP
+.B \-v
+Print the version number and copyright.
+.TP
+.B \-z
+Start with zeroed partition table. This option is useful when you
+want to repartition your entire disk.
+.I Note:
+this option does not zero the partition table on the disk; rather, it
+simply starts the program without reading the existing partition
+table.
+.TP
+.BI \-c " cylinders"
+.TP
+.BI \-h " heads"
+.TP
+.BI \-s " sectors-per-track"
+Override the number of cylinders, heads and sectors per track read
+from the BIOS. If your BIOS or adapter does not supply this
+information or if it supplies incorrect information, use these options
+to set the disk geometry values.
+.TP
+.BI \-P " opt"
+Prints the partition table in specified formats.
+.I opt
+can be one or more of "r", "s" or "t". See the
+.BR p rint
+command (above) for more information on the print formats.
+.SH "EXIT STATUS"
+0: No errors; 1: Invocation error; 2: I/O error;
+3: cannot get geometry; 4: bad partition table on disk.
+.SH "SEE ALSO"
+.BR fdisk (8),
+.BR sfdisk (8),
+.BR mkfs (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR kpartx(8)
+.SH BUGS
+The current version does not support multiple disks.
+.SH AUTHOR
+Kevin E. Martin (martin@cs.unc.edu)
+
+.SH AVAILABILITY
+The cfdisk command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/fdisks/cfdisk.c b/fdisks/cfdisk.c
new file mode 100644
index 0000000..0ceed33
--- /dev/null
+++ b/fdisks/cfdisk.c
@@ -0,0 +1,2854 @@
+/****************************************************************************
+ *
+ * CFDISK
+ *
+ * cfdisk is a curses based disk drive partitioning program that can
+ * create partitions for a wide variety of operating systems including
+ * Linux, MS-DOS and OS/2.
+ *
+ * cfdisk was inspired by the fdisk program, by A. V. Le Blanc
+ * (LeBlanc@mcc.ac.uk).
+ *
+ * Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
+ *
+ * cfdisk is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * cfdisk is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ *
+ * Created: Fri Jan 28 22:46:58 1994, martin@cs.unc.edu
+ * >2GB patches: Sat Feb 11 09:08:10 1995, faith@cs.unc.edu
+ * Prettier menus: Sat Feb 11 09:08:25 1995, Janne Kukonlehto
+ * <jtklehto@stekt.oulu.fi>
+ * Versions 0.8e-p: aeb@cwi.nl
+ * Rebaptised 2.9p, following util-linux versioning.
+ *
+ * Recognition of NTFS / HPFS difference inspired by patches
+ * from Marty Leisner <leisner@sdsp.mc.xerox.com>
+ * Exit codes by Enrique Zanardi <ezanardi@ull.es>:
+ * 0: all went well
+ * 1: command line error, out of memory
+ * 2: hardware problems [Cannot open/seek/read/write disk drive].
+ * 3: ioctl(fd, HDIO_GETGEO,...) failed. (Probably it is not a disk.)
+ * 4: bad partition table on disk. [Bad primary/logical partition].
+ *
+ * Sat, 23 Jan 1999 19:34:45 +0100 <Vincent.Renardias@ldsol.com>
+ * Internationalized + provided initial French translation.
+ * Sat Mar 20 09:26:34 EST 1999 <acme@conectiva.com.br>
+ * Some more i18n.
+ * Sun Jul 18 03:19:42 MEST 1999 <aeb@cwi.nl>
+ * Terabyte-sized disks.
+ * Sat Jun 30 05:23:19 EST 2001 <nathans@sgi.com>
+ * XFS label recognition.
+ * Thu Nov 22 15:42:56 CET 2001 <flavio.stanchina@tin.it>
+ * ext3 and ReiserFS recognition.
+ * Sun Oct 12 17:43:43 CEST 2003 <flavio.stanchina@tin.it>
+ * JFS recognition; ReiserFS label recognition.
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SLANG_H
+#include <slang.h>
+#elif defined(HAVE_SLANG_SLANG_H)
+#include <slang/slang.h>
+#endif
+
+#ifdef HAVE_SLCURSES_H
+#include <slcurses.h>
+#elif defined(HAVE_SLANG_SLCURSES_H)
+#include <slang/slcurses.h>
+#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
+#include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSES_H)
+#include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#include <ncurses/ncurses.h>
+#endif
+
+#include <signal.h>
+#include <math.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LIBBLKID
+#include <blkid.h>
+#endif
+
+#ifdef HAVE_WIDECHAR
+#include <wctype.h>
+#endif
+
+#include "closestream.h"
+#include "nls.h"
+#include "rpmatch.h"
+#include "blkdev.h"
+#include "strutils.h"
+#include "common.h"
+#include "gpt.h"
+#include "mbsalign.h"
+#include "widechar.h"
+
+#ifdef __GNU__
+#define DEFAULT_DEVICE "/dev/hd0"
+#define ALTERNATE_DEVICE "/dev/sd0"
+#elif defined(__FreeBSD__)
+#define DEFAULT_DEVICE "/dev/ad0"
+#define ALTERNATE_DEVICE "/dev/da0"
+#else
+#define DEFAULT_DEVICE "/dev/hda"
+#define ALTERNATE_DEVICE "/dev/sda"
+#endif
+
+/* With K=1024 we have `binary' megabytes, gigabytes, etc.
+ Some misguided hackers like that.
+ With K=1000 we have MB and GB that follow the standards
+ [SI, ATA, IEEE etc] and the disk manufacturers and the law. */
+#define K 1000
+
+#define LINE_LENGTH 80
+#define MAXIMUM_PARTS 60
+
+#define SECTOR_SIZE 512
+
+#define MAX_HEADS 256
+#define MAX_SECTORS 63
+
+#define ACTIVE_FLAG 0x80
+#define PART_TABLE_FLAG0 0x55
+#define PART_TABLE_FLAG1 0xAA
+
+#define UNUSABLE -1
+#define FREE_SPACE 0x00
+#define DOS_EXTENDED 0x05
+#define OS2_OR_NTFS 0x07
+#define WIN98_EXTENDED 0x0f
+#define LINUX_EXTENDED 0x85
+#define LINUX_MINIX 0x81
+#define LINUX_SWAP 0x82
+#define LINUX 0x83
+
+#define PRI_OR_LOG -1
+#define PRIMARY -2
+#define LOGICAL -3
+
+#define COL_ID_WIDTH 25
+
+#define ESC '\033'
+#define DEL '\177'
+#define BELL '\007'
+#define REDRAWKEY '\014' /* ^L */
+
+/* Display units */
+#define GIGABYTES 1
+#define MEGABYTES 2
+#define SECTORS 3
+#define CYLINDERS 4
+
+#define GS_DEFAULT -1
+#define GS_ESCAPE -2
+
+#define PRINT_RAW_TABLE 1
+#define PRINT_SECTOR_TABLE 2
+#define PRINT_PARTITION_TABLE 4
+
+#define IS_PRIMARY(p) ((p) >= 0 && (p) < 4)
+#define IS_LOGICAL(p) ((p) > 3)
+
+#define round_int(d) ((double)((int)(d+0.5)))
+#define ceiling(d) ((double)(((d) != (int)(d)) ? (int)(d+1.0) : (int)(d)))
+
+struct partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char head; /* starting head */
+ unsigned char sector; /* starting sector */
+ unsigned char cyl; /* starting cylinder */
+ unsigned char sys_ind; /* What partition type */
+ unsigned char end_head; /* end head */
+ unsigned char end_sector; /* end sector */
+ unsigned char end_cyl; /* end cylinder */
+ unsigned char start4[4]; /* starting sector counting from 0 */
+ unsigned char size4[4]; /* nr of sectors in partition */
+};
+
+int heads = 0;
+int sectors = 0;
+long long cylinders = 0;
+int cylinder_size = 0; /* heads * sectors */
+long long actual_size = 0; /* (in 512-byte sectors) - set using ioctl */
+ /* explicitly given user values */
+int user_heads = 0, user_sectors = 0;
+long long user_cylinders = 0;
+ /* kernel values; ignore the cylinders */
+int kern_heads = 0, kern_sectors = 0;
+ /* partition-table derived values */
+int pt_heads = 0, pt_sectors = 0;
+
+
+static void
+set_hsc0(unsigned char *h, unsigned char *s, int *c, long long sector) {
+ *s = sector % sectors + 1;
+ sector /= sectors;
+ *h = sector % heads;
+ sector /= heads;
+ *c = sector;
+}
+
+static void
+set_hsc(unsigned char *h, unsigned char *s, unsigned char *c,
+ long long sector) {
+ int cc;
+
+ if (sector >= 1024*cylinder_size)
+ sector = 1024*cylinder_size - 1;
+ set_hsc0(h, s, &cc, sector);
+ *c = cc & 0xFF;
+ *s |= (cc >> 2) & 0xC0;
+}
+
+static void
+set_hsc_begin(struct partition *p, long long sector) {
+ set_hsc(& p->head, & p->sector, & p->cyl, sector);
+}
+
+static void
+set_hsc_end(struct partition *p, long long sector) {
+ set_hsc(& p->end_head, & p->end_sector, & p->end_cyl, sector);
+}
+
+#define is_extended(x) ((x) == DOS_EXTENDED || (x) == WIN98_EXTENDED || \
+ (x) == LINUX_EXTENDED)
+
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static void
+store4_little_endian(unsigned char *cp, unsigned int val) {
+ cp[0] = (val & 0xff);
+ cp[1] = ((val >> 8) & 0xff);
+ cp[2] = ((val >> 16) & 0xff);
+ cp[3] = ((val >> 24) & 0xff);
+}
+
+static unsigned int
+read4_little_endian(unsigned char *cp) {
+ return (unsigned int)(cp[0]) + ((unsigned int)(cp[1]) << 8)
+ + ((unsigned int)(cp[2]) << 16)
+ + ((unsigned int)(cp[3]) << 24);
+}
+
+static void
+set_start_sect(struct partition *p, unsigned int start_sect) {
+ store4_little_endian(p->start4, start_sect);
+}
+
+static unsigned int
+get_start_sect(struct partition *p) {
+ return read4_little_endian(p->start4);
+}
+
+static void
+set_nr_sects(struct partition *p, unsigned int nr_sects) {
+ store4_little_endian(p->size4, nr_sects);
+}
+
+static unsigned int
+get_nr_sects(struct partition *p) {
+ return read4_little_endian(p->size4);
+}
+
+#define ALIGNMENT 2
+typedef union {
+ struct {
+ unsigned char align[ALIGNMENT];
+ unsigned char b[SECTOR_SIZE];
+ } c;
+ struct {
+ unsigned char align[ALIGNMENT];
+ unsigned char buffer[0x1BE];
+ struct partition part[4];
+ unsigned char magicflag[2];
+ } p;
+} partition_table;
+
+typedef struct {
+ long long first_sector; /* first sector in partition */
+ long long last_sector; /* last sector in partition */
+ long offset; /* offset from first sector to start of data */
+ int flags; /* active == 0x80 */
+ int id; /* filesystem type */
+ int num; /* number of partition -- primary vs. logical */
+#define LABELSZ 16
+ char volume_label[LABELSZ+1];
+#define OSTYPESZ 8
+ char ostype[OSTYPESZ+1];
+#define FSTYPESZ 12
+ char fstype[FSTYPESZ+1];
+} partition_info;
+
+char *disk_device = DEFAULT_DEVICE;
+int fd;
+int changed = FALSE;
+int opened = FALSE;
+int opentype;
+int curses_started = 0;
+
+partition_info p_info[MAXIMUM_PARTS];
+partition_info ext_info;
+int num_parts = 0;
+
+int logical = 0;
+long long logical_sectors[MAXIMUM_PARTS];
+
+__sighandler_t old_SIGINT, old_SIGTERM;
+
+int arrow_cursor = FALSE;
+int display_units = MEGABYTES;
+int zero_table = FALSE;
+int use_partition_table_geometry = FALSE;
+int print_only = 0;
+
+/* Curses screen information */
+int cur_part = 0;
+int warning_last_time = FALSE;
+int defined = FALSE;
+int COLUMNS = 80;
+int NUM_ON_SCREEN = 1;
+
+/* Y coordinates */
+int HEADER_START = 0;
+int DISK_TABLE_START = 6;
+int WARNING_START = 23;
+int COMMAND_LINE_Y = 21;
+
+/* X coordinates */
+int NAME_START = 4;
+int FLAGS_START = 16;
+int PTYPE_START = 28;
+int FSTYPE_START = 38;
+int LABEL_START = 54;
+int SIZE_START = 68;
+int COMMAND_LINE_X = 5;
+
+static void die_x(int ret);
+static void draw_screen(void);
+
+/* Guaranteed alloc */
+static void *
+xmalloc (size_t size) {
+ void *t;
+
+ if (size == 0)
+ return NULL;
+
+ t = malloc (size);
+ if (t == NULL) {
+ fprintf (stderr, _("%s: Out of memory!\n"), "cfdisk");
+ die_x(1);
+ }
+ return t;
+}
+
+/* Some libc's have their own basename() */
+static char *
+my_basename(char *devname) {
+ char *s = strrchr(devname, '/');
+ return s ? s+1 : devname;
+}
+
+static char *
+partition_type_name(unsigned char type) {
+ struct systypes *s = i386_sys_types;
+
+ while(s->name && s->type != type)
+ s++;
+ return s->name;
+}
+
+static char *
+partition_type_text(int i) {
+ if (p_info[i].id == UNUSABLE)
+ return _("Unusable");
+ else if (p_info[i].id == FREE_SPACE)
+ return _("Free Space");
+ else if (*p_info[i].fstype)
+ return p_info[i].fstype;
+ else
+ return _(partition_type_name(p_info[i].id));
+}
+
+static void
+fdexit(int ret) {
+ if (opened) {
+ if (changed)
+ fsync(fd);
+ close(fd);
+ }
+ if (changed) {
+ fprintf(stderr, _("Disk has been changed.\n"));
+#if 0
+ fprintf(stderr, _("Reboot the system to ensure the partition "
+ "table is correctly updated.\n"));
+#endif
+
+ fprintf( stderr, _("\nWARNING: If you have created or modified any\n"
+ "DOS 6.x partitions, please see the cfdisk manual\n"
+ "page for additional information.\n") );
+ }
+
+ exit(ret);
+}
+
+/*
+ * Note that @len is size of @str buffer.
+ *
+ * Returns number of read bytes (without \0).
+ */
+static int
+get_string(char *str, int len, char *def) {
+ size_t cells = 0;
+ ssize_t i = 0;
+ int x, y;
+ int use_def = FALSE;
+ wint_t c;
+
+ getyx(stdscr, y, x);
+ clrtoeol();
+
+ str[i] = 0;
+
+ if (def != NULL) {
+ mvaddstr(y, x, def);
+ move(y, x);
+ use_def = TRUE;
+ }
+
+ refresh();
+
+ while (1) {
+#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
+ defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
+ if (get_wch(&c) == ERR) {
+#else
+ if ((c = getch()) == ERR) {
+#endif
+ if (!isatty(STDIN_FILENO))
+ exit(2);
+ else
+ break;
+ }
+ if (c == '\r' || c == '\n' || c == KEY_ENTER)
+ break;
+
+ switch (c) {
+ case ESC:
+ move(y, x);
+ clrtoeol();
+ refresh();
+ return GS_ESCAPE;
+ case DEL:
+ case '\b':
+ case KEY_BACKSPACE:
+ if (i > 0) {
+ cells--;
+ i = mbs_truncate(str, &cells);
+ if (i < 0)
+ return GS_ESCAPE;
+ mvaddch(y, x + cells, ' ');
+ move(y, x + cells);
+ } else if (use_def) {
+ clrtoeol();
+ use_def = FALSE;
+ } else
+ putchar(BELL);
+ break;
+ default:
+#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
+ if (i + 1 < len && iswprint(c)) {
+ wchar_t wc = (wchar_t) c;
+ char s[MB_CUR_MAX + 1];
+ int sz = wctomb(s, wc);
+
+ if (sz > 0 && sz + i < len) {
+ s[sz] = '\0';
+ mvaddnstr(y, x + cells, s, sz);
+ if (use_def) {
+ clrtoeol();
+ use_def = FALSE;
+ }
+ memcpy(str + i, s, sz);
+ i += sz;
+ str[i] = '\0';
+ cells += wcwidth(wc);
+ } else
+ putchar(BELL);
+ }
+#else
+ if (i + 1 < len && isprint(c)) {
+ mvaddch(y, x + cells, c);
+ if (use_def) {
+ clrtoeol();
+ use_def = FALSE;
+ }
+ str[i++] = c;
+ str[i] = 0;
+ cells++;
+ }
+#endif
+ else
+ putchar(BELL);
+ }
+ refresh();
+ }
+
+ if (use_def)
+ return GS_DEFAULT;
+ else
+ return i;
+}
+
+static void
+clear_warning(void) {
+ int i;
+
+ if (!curses_started || !warning_last_time)
+ return;
+
+ move(WARNING_START,0);
+ for (i = 0; i < COLS; i++)
+ addch(' ');
+
+ warning_last_time = FALSE;
+}
+
+static void
+print_warning(char *s) {
+ if (!curses_started) {
+ fprintf(stderr, "%s\n", s);
+ } else {
+ mvaddstr(WARNING_START, (COLS-strlen(s))/2, s);
+ putchar(BELL); /* CTRL-G */
+
+ warning_last_time = TRUE;
+ }
+}
+
+static void
+fatal(char *s, int ret) {
+ char *err1 = _("FATAL ERROR");
+ char *err2 = _("Press any key to exit cfdisk");
+
+ if (curses_started) {
+ char *str = xmalloc(strlen(s) + strlen(err1) + strlen(err2) + 10);
+
+ sprintf(str, "%s: %s", err1, s);
+ if (strlen(str) > (size_t) COLS)
+ str[COLS] = 0;
+ mvaddstr(WARNING_START, (COLS-strlen(str))/2, str);
+ sprintf(str, "%s", err2);
+ if (strlen(str) > (size_t) COLS)
+ str[COLS] = 0;
+ mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str);
+ putchar(BELL); /* CTRL-G */
+ refresh();
+ (void)getch();
+ die_x(ret);
+ } else {
+ fprintf(stderr, "%s: %s\n", err1, s);
+ exit(ret);
+ }
+}
+
+static void
+die(int dummy __attribute__((__unused__))) {
+ die_x(0);
+}
+
+static void
+die_x(int ret) {
+ signal(SIGINT, old_SIGINT);
+ signal(SIGTERM, old_SIGTERM);
+#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
+ SLsmg_gotorc(LINES-1, 0);
+ SLsmg_refresh();
+#else
+ mvcur(0, COLS-1, LINES-1, 0);
+#endif
+ nl();
+ endwin();
+ printf("\n");
+ fdexit(ret);
+}
+
+static void
+read_sector(unsigned char *buffer, long long sect_num) {
+ if (lseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
+ fatal(_("Cannot seek on disk drive"), 2);
+ if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
+ fatal(_("Cannot read disk drive"), 2);
+}
+
+static void
+write_sector(unsigned char *buffer, long long sect_num) {
+ if (lseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
+ fatal(_("Cannot seek on disk drive"), 2);
+ if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
+ fatal(_("Cannot write disk drive"), 2);
+}
+
+#ifdef HAVE_LIBBLKID
+static void
+get_fsinfo(int i)
+{
+ blkid_probe pr;
+ blkid_loff_t offset, size;
+ const char *data;
+
+ offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE;
+ size = (p_info[i].last_sector - p_info[i].first_sector + 1) * SECTOR_SIZE;
+ pr = blkid_new_probe();
+ if (!pr)
+ return;
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
+ BLKID_SUBLKS_TYPE);
+ if (blkid_probe_set_device(pr, fd, offset, size))
+ goto done;
+ if (blkid_do_safeprobe(pr))
+ goto done;
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &data, 0))
+ strncpy(p_info[i].fstype, data, FSTYPESZ);
+
+ if (!blkid_probe_lookup_value(pr, "LABEL", &data, 0))
+ strncpy(p_info[i].volume_label, data, LABELSZ);
+done:
+ blkid_free_probe(pr);
+}
+#endif
+
+static void
+check_part_info(void) {
+ int i, pri = 0, log = 0;
+
+ for (i = 0; i < num_parts; i++)
+ if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
+ pri++;
+ else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
+ log++;
+ if (is_extended(ext_info.id)) {
+ if (log > 0)
+ pri++;
+ else {
+ ext_info.first_sector = 0;
+ ext_info.last_sector = 0;
+ ext_info.offset = 0;
+ ext_info.flags = 0;
+ ext_info.id = FREE_SPACE;
+ ext_info.num = PRIMARY;
+ }
+ }
+
+ if (pri >= 4) {
+ for (i = 0; i < num_parts; i++)
+ if (p_info[i].id == FREE_SPACE || p_info[i].id == UNUSABLE) {
+ if (is_extended(ext_info.id)) {
+ if (p_info[i].first_sector >= ext_info.first_sector &&
+ p_info[i].last_sector <= ext_info.last_sector) {
+ p_info[i].id = FREE_SPACE;
+ p_info[i].num = LOGICAL;
+ } else if (i > 0 &&
+ p_info[i-1].first_sector >=
+ ext_info.first_sector &&
+ p_info[i-1].last_sector <=
+ ext_info.last_sector) {
+ p_info[i].id = FREE_SPACE;
+ p_info[i].num = LOGICAL;
+ } else if (i < num_parts-1 &&
+ p_info[i+1].first_sector >=
+ ext_info.first_sector &&
+ p_info[i+1].last_sector <=
+ ext_info.last_sector) {
+ p_info[i].id = FREE_SPACE;
+ p_info[i].num = LOGICAL;
+ } else
+ p_info[i].id = UNUSABLE;
+ } else /* if (!is_extended(ext_info.id)) */
+ p_info[i].id = UNUSABLE;
+ } else /* if (p_info[i].id > 0) */
+ while (0); /* Leave these alone */
+ } else { /* if (pri < 4) */
+ for (i = 0; i < num_parts; i++) {
+ if (p_info[i].id == UNUSABLE)
+ p_info[i].id = FREE_SPACE;
+ if (p_info[i].id == FREE_SPACE) {
+ if (is_extended(ext_info.id)) {
+ if (p_info[i].first_sector >= ext_info.first_sector &&
+ p_info[i].last_sector <= ext_info.last_sector)
+ p_info[i].num = LOGICAL;
+ else if (i > 0 &&
+ p_info[i-1].first_sector >=
+ ext_info.first_sector &&
+ p_info[i-1].last_sector <=
+ ext_info.last_sector)
+ p_info[i].num = PRI_OR_LOG;
+ else if (i < num_parts-1 &&
+ p_info[i+1].first_sector >=
+ ext_info.first_sector &&
+ p_info[i+1].last_sector <=
+ ext_info.last_sector)
+ p_info[i].num = PRI_OR_LOG;
+ else
+ p_info[i].num = PRIMARY;
+ } else /* if (!is_extended(ext_info.id)) */
+ p_info[i].num = PRI_OR_LOG;
+ } else /* if (p_info[i].id > 0) */
+ while (0); /* Leave these alone */
+ }
+ }
+}
+
+static void
+remove_part(int i) {
+ int p;
+
+ for (p = i; p < num_parts; p++)
+ p_info[p] = p_info[p+1];
+
+ num_parts--;
+ if (cur_part == num_parts)
+ cur_part--;
+}
+
+static void
+insert_empty_part(int i, long long first, long long last) {
+ int p;
+
+ for (p = num_parts; p > i; p--)
+ p_info[p] = p_info[p-1];
+
+ p_info[i].first_sector = first;
+ p_info[i].last_sector = last;
+ p_info[i].offset = 0;
+ p_info[i].flags = 0;
+ p_info[i].id = FREE_SPACE;
+ p_info[i].num = PRI_OR_LOG;
+ p_info[i].volume_label[0] = 0;
+ p_info[i].fstype[0] = 0;
+ p_info[i].ostype[0] = 0;
+
+ num_parts++;
+}
+
+static void
+del_part(int i) {
+ int num = p_info[i].num;
+
+ if (i > 0 && (p_info[i-1].id == FREE_SPACE ||
+ p_info[i-1].id == UNUSABLE)) {
+ /* Merge with previous partition */
+ p_info[i-1].last_sector = p_info[i].last_sector;
+ remove_part(i--);
+ }
+
+ if (i < num_parts - 1 && (p_info[i+1].id == FREE_SPACE ||
+ p_info[i+1].id == UNUSABLE)) {
+ /* Merge with next partition */
+ p_info[i+1].first_sector = p_info[i].first_sector;
+ remove_part(i);
+ }
+
+ if (i > 0)
+ p_info[i].first_sector = p_info[i-1].last_sector + 1;
+ else
+ p_info[i].first_sector = 0;
+
+ if (i < num_parts - 1)
+ p_info[i].last_sector = p_info[i+1].first_sector - 1;
+ else
+ p_info[i].last_sector = actual_size - 1;
+
+ p_info[i].offset = 0;
+ p_info[i].flags = 0;
+ p_info[i].id = FREE_SPACE;
+ p_info[i].num = PRI_OR_LOG;
+
+ if (IS_LOGICAL(num)) {
+ /* We have a logical partition --> shrink the extended partition
+ * if (1) this is the first logical drive, or (2) this is the
+ * last logical drive; and if there are any other logical drives
+ * then renumber the ones after "num".
+ */
+ if (i == 0 || (i > 0 && IS_PRIMARY(p_info[i-1].num))) {
+ ext_info.first_sector = p_info[i].last_sector + 1;
+ ext_info.offset = 0;
+ }
+ if (i == num_parts-1 ||
+ (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)))
+ ext_info.last_sector = p_info[i].first_sector - 1;
+ for (i = 0; i < num_parts; i++)
+ if (p_info[i].num > num)
+ p_info[i].num--;
+ }
+
+ /* Clean up the rest of the partitions */
+ check_part_info();
+}
+
+static int
+add_part(int num, int id, int flags, long long first, long long last,
+ long long offset, int want_label, char **errmsg) {
+ int i, pri = 0, log = 0;
+
+ if (num_parts == MAXIMUM_PARTS) {
+ *errmsg = _("Too many partitions");
+ return -1;
+ }
+
+ if (first < 0) {
+ *errmsg = _("Partition begins before sector 0");
+ return -1;
+ }
+
+ if (last < 0) {
+ *errmsg = _("Partition ends before sector 0");
+ return -1;
+ }
+
+ if (first >= actual_size) {
+ *errmsg = _("Partition begins after end-of-disk");
+ return -1;
+ }
+
+ if (last >= actual_size) {
+ *errmsg = _("Partition ends after end-of-disk");
+ return -1;
+ }
+
+ for (i = 0; i < num_parts; i++) {
+ if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
+ pri++;
+ else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
+ log++;
+ }
+ if (is_extended(ext_info.id) && log > 0)
+ pri++;
+
+ if (IS_PRIMARY(num)) {
+ if (pri >= 4) {
+ return -1; /* no room for more */
+ } else
+ pri++;
+ }
+
+ for (i = 0; i < num_parts && p_info[i].last_sector < first; i++);
+
+ if (i < num_parts && p_info[i].id != FREE_SPACE) {
+ if (last < p_info[i].first_sector)
+ *errmsg = _("logical partitions not in disk order");
+ else if (first + offset <= p_info[i].last_sector &&
+ p_info[i].first_sector + p_info[i].offset <= last)
+ *errmsg = _("logical partitions overlap");
+ else
+ /* the enlarged logical partition starts at the
+ partition table sector that defines it */
+ *errmsg = _("enlarged logical partitions overlap");
+ return -1;
+ }
+
+ if (i == num_parts || last > p_info[i].last_sector) {
+ return -1;
+ }
+
+ if (is_extended(id)) {
+ if (ext_info.id != FREE_SPACE) {
+ return -1; /* second extended */
+ }
+ else if (IS_PRIMARY(num)) {
+ ext_info.first_sector = first;
+ ext_info.last_sector = last;
+ ext_info.offset = offset;
+ ext_info.flags = flags;
+ ext_info.id = id;
+ ext_info.num = num;
+ ext_info.volume_label[0] = 0;
+ ext_info.fstype[0] = 0;
+ ext_info.ostype[0] = 0;
+ return 0;
+ } else {
+ return -1; /* explicit extended logical */
+ }
+ }
+
+ if (IS_LOGICAL(num)) {
+ if (!is_extended(ext_info.id)) {
+ print_warning(_("!!!! Internal error creating logical "
+ "drive with no extended partition !!!!"));
+ } else {
+ /* We might have a logical partition outside of the extended
+ * partition's range --> we have to extend the extended
+ * partition's range to encompass this new partition, but we
+ * must make sure that there are no primary partitions between
+ * it and the closest logical drive in extended partition.
+ */
+ if (first < ext_info.first_sector) {
+ if (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)) {
+ print_warning(_("Cannot create logical drive here -- would create two extended partitions"));
+ return -1;
+ } else {
+ if (first == 0) {
+ ext_info.first_sector = 0;
+ ext_info.offset = first = offset;
+ } else {
+ ext_info.first_sector = first;
+ }
+ }
+ } else if (last > ext_info.last_sector) {
+ if (i > 0 && IS_PRIMARY(p_info[i-1].num)) {
+ print_warning(_("Cannot create logical drive here -- would create two extended partitions"));
+ return -1;
+ } else {
+ ext_info.last_sector = last;
+ }
+ }
+ }
+ }
+
+ if (first != p_info[i].first_sector &&
+ !(IS_LOGICAL(num) && first == offset)) {
+ insert_empty_part(i, p_info[i].first_sector, first-1);
+ i++;
+ }
+
+ if (last != p_info[i].last_sector)
+ insert_empty_part(i+1, last+1, p_info[i].last_sector);
+
+ p_info[i].first_sector = first;
+ p_info[i].last_sector = last;
+ p_info[i].offset = offset;
+ p_info[i].flags = flags;
+ p_info[i].id = id;
+ p_info[i].num = num;
+ p_info[i].volume_label[0] = 0;
+ p_info[i].fstype[0] = 0;
+ p_info[i].ostype[0] = 0;
+
+#ifdef HAVE_LIBBLKID
+ if (want_label)
+ get_fsinfo(i);
+#endif
+ check_part_info();
+
+ return 0;
+}
+
+static int
+find_primary(void) {
+ int num = 0, cur = 0;
+
+ while (cur < num_parts && IS_PRIMARY(num))
+ if ((p_info[cur].id > 0 && p_info[cur].num == num) ||
+ (is_extended(ext_info.id) && ext_info.num == num)) {
+ num++;
+ cur = 0;
+ } else
+ cur++;
+
+ if (!IS_PRIMARY(num))
+ return -1;
+ else
+ return num;
+}
+
+static int
+find_logical(int i) {
+ int num = -1;
+ int j;
+
+ for (j = i; j < num_parts && num == -1; j++)
+ if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
+ num = p_info[j].num;
+
+ if (num == -1) {
+ num = 4;
+ for (j = 0; j < num_parts; j++)
+ if (p_info[j].id > 0 && p_info[j].num == num)
+ num++;
+ }
+
+ return num;
+}
+
+/*
+ * Command menu support by Janne Kukonlehto <jtklehto@phoenix.oulu.fi>
+ * September 1994
+ */
+
+/* Constants for menuType parameter of menuSelect function */
+#define MENU_ACCEPT_OTHERS 4
+#define MENU_BUTTON 8
+/* Miscellenous constants */
+#define MENU_SPACING 2
+#define MENU_MAX_ITEMS 256 /* for simpleMenu function */
+
+struct MenuItem
+{
+ int key; /* Keyboard shortcut; if zero, then there is no more items in the menu item table */
+ char *name; /* Item name, should be eight characters with current implementation */
+ char *desc; /* Item description to be printed when item is selected */
+};
+
+/*
+ * Actual function which prints the button bar and highlights the active button
+ * Should not be called directly. Call function menuSelect instead.
+ */
+
+static int
+menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength,
+ char *available, int menuType, int current ) {
+ int i, lmargin = x;
+ char *mcd;
+
+ /* Print available buttons */
+ move( y, x ); clrtoeol();
+
+ for( i = 0; menuItems[i].key; i++ ) {
+ char buff[20];
+ int lenName;
+ const char *mi;
+
+ /* Search next available button */
+ while( menuItems[i].key && !strchr(available, menuItems[i].key) )
+ i++;
+
+ if( !menuItems[i].key ) break; /* No more menu items */
+
+ /* If selected item is not available and we have bypassed it,
+ make current item selected */
+ if( current < i && menuItems[current].key < 0 ) current = i;
+
+ /* If current item is selected, highlight it */
+ if( current == i ) /*attron( A_REVERSE )*/ standout ();
+
+ /* Print item */
+ /* Because of a bug in gettext() we must not translate empty strings */
+ if (menuItems[i].name[0])
+ mi = _(menuItems[i].name);
+ else
+ mi = "";
+ lenName = strlen( mi );
+#if 0
+ if(lenName > itemLength || lenName >= sizeof(buff))
+ print_warning(_("Menu item too long. Menu may look odd."));
+#endif
+ if ((size_t) lenName >= sizeof(buff)) { /* truncate ridiculously long string */
+ xstrncpy(buff, mi, sizeof(buff));
+ } else if (lenName >= itemLength) {
+ snprintf(buff, sizeof(buff),
+ (menuType & MENU_BUTTON) ? "[%s]" : "%s", mi);
+ } else {
+ snprintf(buff, sizeof(buff),
+ (menuType & MENU_BUTTON) ? "[%*s%-*s]" : "%*s%-*s",
+ (itemLength - lenName) / 2, "",
+ (itemLength - lenName + 1) / 2 + lenName, mi);
+ }
+ mvaddstr( y, x, buff );
+
+ /* Lowlight after selected item */
+ if( current == i ) /*attroff( A_REVERSE )*/ standend ();
+
+ /* Calculate position for the next item */
+ x += itemLength + MENU_SPACING;
+ if( menuType & MENU_BUTTON ) x += 2;
+ if( x > COLUMNS - lmargin - 12 )
+ {
+ x = lmargin;
+ y ++ ;
+ }
+ }
+
+ /* Print the description of selected item */
+ mcd = _(menuItems[current].desc);
+ mvaddstr( WARNING_START + 1, (COLUMNS - strlen( mcd )) / 2, mcd );
+ return y;
+}
+
+/* This function takes a list of menu items, lets the user choose one *
+ * and returns the keyboard shortcut value of the selected menu item */
+
+static int
+menuSelect( int y, int x, struct MenuItem *menuItems, int itemLength,
+ char *available, int menuType, int menuDefault ) {
+ int i, ylast = y, key = 0, current = menuDefault;
+
+ /* Make sure that the current is one of the available items */
+ while( !strchr(available, menuItems[current].key) ) {
+ current ++ ;
+ if( !menuItems[current].key ) current = 0;
+ }
+
+ keypad(stdscr, TRUE);
+
+ /* Repeat until allowable choice has been made */
+ while( !key ) {
+ /* Display the menu and read a command */
+ ylast = menuUpdate( y, x, menuItems, itemLength, available,
+ menuType, current );
+ refresh();
+ key = getch();
+
+ if (key == ERR)
+ if (!isatty(STDIN_FILENO))
+ exit(2);
+
+ /* Clear out all prompts and such */
+ clear_warning();
+ for (i = y; i < ylast; i++) {
+ move(i, x);
+ clrtoeol();
+ }
+ move( WARNING_START + 1, 0 );
+ clrtoeol();
+
+ switch (key) {
+ case KEY_RIGHT:
+ case '\t':
+ /* Select next menu item */
+ do {
+ current++;
+ if (!menuItems[current].key)
+ current = 0;
+ } while (!strchr(available, menuItems[current].key));
+ key = 0;
+ break;
+ case KEY_LEFT:
+#ifdef KEY_BTAB
+ case KEY_BTAB: /* Back tab */
+#endif
+ /* Select previous menu item */
+ do {
+ current--;
+ if (current < 0) {
+ while (menuItems[current + 1].key)
+ current++;
+ }
+ } while (!strchr(available, menuItems[current].key));
+ key = 0;
+ break;
+ case KEY_ENTER:
+ case '\n':
+ case '\r':
+ /* Enter equals the keyboard shortcut of current menu item */
+ key = menuItems[current].key;
+ break;
+ }
+
+ /* Should all keys to be accepted? */
+ if( key && (menuType & MENU_ACCEPT_OTHERS) ) break;
+
+ /* Is pressed key among acceptable ones? */
+ if( key && (strchr(available, tolower(key)) || strchr(available, key)))
+ break;
+
+ /* The key has not been accepted so far -> let's reject it */
+ if (key) {
+ key = 0;
+ putchar( BELL );
+ print_warning(_("Illegal key"));
+ }
+ }
+
+ keypad(stdscr, FALSE);
+
+ /* Clear out prompts and such */
+ clear_warning();
+ for( i = y; i <= ylast; i ++ ) {
+ move( i, x );
+ clrtoeol();
+ }
+ move( WARNING_START + 1, 0 );
+ clrtoeol();
+ return key;
+}
+
+/* A function which displays "Press a key to continue" *
+ * and waits for a keypress. *
+ * Perhaps calling function menuSelect is a bit overkill but who cares? */
+
+static void
+menuContinue(void) {
+ static struct MenuItem menuContinueBtn[]=
+ {
+ { 'c', "", N_("Press a key to continue") },
+ { 0, NULL, NULL }
+ };
+
+ menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X,
+ menuContinueBtn, 0, "c", MENU_ACCEPT_OTHERS, 0 );
+}
+
+/* Function menuSelect takes way too many parameters *
+ * Luckily, most of time we can do with this function */
+
+static int
+menuSimple(struct MenuItem *menuItems, int menuDefault) {
+ int i, j, itemLength = 0;
+ char available[MENU_MAX_ITEMS];
+
+ for(i = 0; menuItems[i].key; i++)
+ {
+ j = strlen( _(menuItems[i].name) );
+ if( j > itemLength ) itemLength = j;
+ available[i] = menuItems[i].key;
+ }
+ available[i] = 0;
+ return menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuItems, itemLength,
+ available, MENU_BUTTON, menuDefault);
+}
+
+/* End of command menu support code */
+
+static void
+new_part(int i) {
+ char response[LINE_LENGTH], def[LINE_LENGTH];
+ char c;
+ long long first = p_info[i].first_sector;
+ long long last = p_info[i].last_sector;
+ long long offset = 0;
+ int flags = 0;
+ int id = LINUX;
+ int num = -1;
+ long long num_sects = last - first + 1;
+ int len, ext, j;
+ char *errmsg;
+ double sectors_per_MB = K*K / 512.0;
+
+ if (p_info[i].num == PRI_OR_LOG) {
+ static struct MenuItem menuPartType[]=
+ {
+ { 'p', N_("Primary"), N_("Create a new primary partition") },
+ { 'l', N_("Logical"), N_("Create a new logical partition") },
+ { ESC, N_("Cancel"), N_("Don't create a partition") },
+ { 0, NULL, NULL }
+ };
+
+ c = menuSimple( menuPartType, 0 );
+ if (toupper(c) == 'P')
+ num = find_primary();
+ else if (toupper(c) == 'L')
+ num = find_logical(i);
+ else
+ return;
+ } else if (p_info[i].num == PRIMARY)
+ num = find_primary();
+ else if (p_info[i].num == LOGICAL)
+ num = find_logical(i);
+ else
+ print_warning(_("!!! Internal error !!!"));
+
+ snprintf(def, sizeof(def), "%.2f", num_sects/sectors_per_MB);
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, _("Size (in MB): "));
+ if ((len = get_string(response, LINE_LENGTH, def)) <= 0 &&
+ len != GS_DEFAULT)
+ return;
+ else if (len > 0) {
+#define num_cyls(bytes) (round_int(bytes/SECTOR_SIZE/cylinder_size))
+ for (j = 0;
+ j < len-1 && (isdigit(response[j]) || response[j] == '.');
+ j++);
+ if (toupper(response[j]) == 'K') {
+ num_sects = num_cyls(atof(response)*K)*cylinder_size;
+ } else if (toupper(response[j]) == 'M') {
+ num_sects = num_cyls(atof(response)*K*K)*cylinder_size;
+ } else if (toupper(response[j]) == 'G') {
+ num_sects = num_cyls(atof(response)*K*K*K)*cylinder_size;
+ } else if (toupper(response[j]) == 'C') {
+ num_sects = round_int(atof(response))*cylinder_size;
+ } else if (toupper(response[j]) == 'S') {
+ num_sects = round_int(atof(response));
+ } else {
+ num_sects = num_cyls(atof(response)*K*K)*cylinder_size;
+ }
+ }
+
+ if (num_sects <= 0 ||
+ num_sects > p_info[i].last_sector - p_info[i].first_sector + 1)
+ return;
+
+ move( COMMAND_LINE_Y, COMMAND_LINE_X ); clrtoeol();
+ if (num_sects < p_info[i].last_sector - p_info[i].first_sector + 1) {
+ /* Determine where inside free space to put partition.
+ */
+ static struct MenuItem menuPlace[]=
+ {
+ { 'b', N_("Beginning"), N_("Add partition at beginning of free space") },
+ { 'e', N_("End"), N_("Add partition at end of free space") },
+ { ESC, N_("Cancel"), N_("Don't create a partition") },
+ { 0, NULL, NULL }
+ };
+ c = menuSimple( menuPlace, 0 );
+ if (toupper(c) == 'B')
+ last = first + num_sects - 1;
+ else if (toupper(c) == 'E')
+ first = last - num_sects + 1;
+ else
+ return;
+ }
+
+ if (IS_LOGICAL(num) && !is_extended(ext_info.id)) {
+ /* We want to add a logical partition, but need to create an
+ * extended partition first.
+ */
+ if ((ext = find_primary()) < 0) {
+ print_warning(_("No room to create the extended partition"));
+ return;
+ }
+ errmsg = 0;
+ if (add_part(ext, DOS_EXTENDED, 0, first, last,
+ (first == 0 ? sectors : 0), 0, &errmsg) && errmsg)
+ print_warning(errmsg);
+ first = ext_info.first_sector + ext_info.offset;
+ }
+
+ /* increment number of all partitions past this one */
+ if (IS_LOGICAL(num)) {
+#if 0
+ /* original text - ok, but fails when partitions not in disk order */
+ for (j = i; j < num_parts; j++)
+ if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
+ p_info[j].num++;
+#else
+ /* always ok */
+ for (j = 0; j < num_parts; j++)
+ if (p_info[j].id > 0 && p_info[j].num >= num)
+ p_info[j].num++;
+#endif
+ }
+
+ /* Now we have a complete partition to ourselves */
+ if (first == 0 || IS_LOGICAL(num))
+ offset = sectors;
+
+ errmsg = 0;
+ if (add_part(num, id, flags, first, last, offset, 0, &errmsg) && errmsg)
+ print_warning(errmsg);
+}
+
+static void
+get_kernel_geometry(void) {
+#ifdef HDIO_GETGEO
+ struct hd_geometry geometry;
+
+ if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+ kern_heads = geometry.heads;
+ kern_sectors = geometry.sectors;
+ }
+#endif
+}
+
+static int
+said_yes(char answer) {
+ char reply[2];
+
+ reply[0] = answer;
+ reply[1] = 0;
+
+ return (rpmatch(reply) == 1) ? 1 : 0;
+}
+
+static void
+get_partition_table_geometry(partition_table *bufp) {
+ struct partition *p;
+ int i,h,s,hh,ss;
+ int first = TRUE;
+ int bad = FALSE;
+
+ for (i=0; i<66; i++)
+ if (bufp->c.b[446+i])
+ goto nonz;
+
+ /* zero table */
+ if (!curses_started) {
+ fatal(_("No partition table.\n"), 3);
+ return;
+ } else {
+ mvaddstr(WARNING_START, 0,
+ _("No partition table. Starting with zero table."));
+ putchar(BELL);
+ refresh();
+ zero_table = TRUE;
+ return;
+ }
+ nonz:
+ if (bufp->p.magicflag[0] != PART_TABLE_FLAG0 ||
+ bufp->p.magicflag[1] != PART_TABLE_FLAG1) {
+ if (!curses_started)
+ fatal(_("Bad signature on partition table"), 3);
+
+ /* Matthew Wilcox */
+ mvaddstr(WARNING_START, 0,
+ _("Unknown partition table type"));
+ mvaddstr(WARNING_START+1, 0,
+ _("Do you wish to start with a zero table [y/N] ?"));
+ putchar(BELL);
+ refresh();
+ {
+ int cont = getch();
+ if (cont == EOF || !said_yes(cont))
+ die_x(3);
+ }
+ zero_table = TRUE;
+ return;
+ }
+
+ hh = ss = 0;
+ for (i=0; i<4; i++) {
+ p = &(bufp->p.part[i]);
+ if (p->sys_ind != 0) {
+ h = p->end_head + 1;
+ s = (p->end_sector & 077);
+ if (first) {
+ hh = h;
+ ss = s;
+ first = FALSE;
+ } else if (hh != h || ss != s)
+ bad = TRUE;
+ }
+ }
+
+ if (!first && !bad) {
+ pt_heads = hh;
+ pt_sectors = ss;
+ }
+}
+
+static void
+decide_on_geometry(void) {
+ heads = (user_heads ? user_heads :
+ pt_heads ? pt_heads :
+ kern_heads ? kern_heads : 255);
+ sectors = (user_sectors ? user_sectors :
+ pt_sectors ? pt_sectors :
+ kern_sectors ? kern_sectors : 63);
+ cylinder_size = heads*sectors;
+ cylinders = actual_size/cylinder_size;
+ if (user_cylinders > 0)
+ cylinders = user_cylinders;
+
+ if (cylinder_size * cylinders > actual_size)
+ print_warning(_("You specified more cylinders than fit on disk"));
+}
+
+static void
+clear_p_info(void) {
+ num_parts = 1;
+ p_info[0].first_sector = 0;
+ p_info[0].last_sector = actual_size - 1;
+ p_info[0].offset = 0;
+ p_info[0].flags = 0;
+ p_info[0].id = FREE_SPACE;
+ p_info[0].num = PRI_OR_LOG;
+
+ ext_info.first_sector = 0;
+ ext_info.last_sector = 0;
+ ext_info.offset = 0;
+ ext_info.flags = 0;
+ ext_info.id = FREE_SPACE;
+ ext_info.num = PRIMARY;
+}
+
+static void
+fill_p_info(void) {
+ int pn, i;
+ long long bs, bsz;
+ unsigned long long llsectors;
+ struct partition *p;
+ partition_table buffer;
+ partition_info tmp_ext;
+
+ memset(&tmp_ext, 0, sizeof tmp_ext);
+ tmp_ext.id = FREE_SPACE;
+ tmp_ext.num = PRIMARY;
+
+ if ((fd = open(disk_device, O_RDWR)) < 0) {
+ if ((fd = open(disk_device, O_RDONLY)) < 0)
+ fatal(_("Cannot open disk drive"), 2);
+ opentype = O_RDONLY;
+ print_warning(_("Opened disk read-only - you have no permission to write"));
+ if (curses_started) {
+ refresh();
+ getch();
+ clear_warning();
+ }
+ } else
+ opentype = O_RDWR;
+ opened = TRUE;
+
+ if (gpt_probe_signature_fd(fd)) {
+ print_warning(_("Warning!! Unsupported GPT (GUID Partition Table) detected. Use GNU Parted."));
+ refresh();
+ getch();
+ clear_warning();
+ }
+
+#ifdef BLKFLSBUF
+ /* Blocks are visible in more than one way:
+ e.g. as block on /dev/hda and as block on /dev/hda3
+ By a bug in the Linux buffer cache, we will see the old
+ contents of /dev/hda when the change was made to /dev/hda3.
+ In order to avoid this, discard all blocks on /dev/hda.
+ Note that partition table blocks do not live in /dev/hdaN,
+ so this only plays a role if we want to show volume labels. */
+ ioctl(fd, BLKFLSBUF); /* ignore errors */
+ /* e.g. Permission Denied */
+#endif
+
+ if (blkdev_get_sectors(fd, &llsectors) == -1)
+ fatal(_("Cannot get disk size"), 3);
+ actual_size = llsectors;
+
+ read_sector(buffer.c.b, 0);
+
+ get_kernel_geometry();
+
+ if (!zero_table || use_partition_table_geometry)
+ get_partition_table_geometry(& buffer);
+
+ decide_on_geometry();
+
+ clear_p_info();
+
+ if (!zero_table) {
+ char *errmsg = "";
+
+ for (i = 0; i < 4; i++) {
+ p = & buffer.p.part[i];
+ bs = get_start_sect(p);
+ bsz = get_nr_sects(p);
+
+ if (p->sys_ind > 0 &&
+ add_part(i, p->sys_ind, p->boot_ind,
+ ((bs <= sectors) ? 0 : bs), bs + bsz - 1,
+ ((bs <= sectors) ? bs : 0), 1, &errmsg)) {
+ char *bad = _("Bad primary partition");
+ char *msg = (char *) xmalloc(strlen(bad) + strlen(errmsg) + 30);
+ sprintf(msg, "%s %d: %s", bad, i + 1, errmsg);
+ fatal(msg, 4);
+ }
+ if (is_extended(buffer.p.part[i].sys_ind))
+ tmp_ext = ext_info;
+ }
+
+ if (is_extended(tmp_ext.id)) {
+ ext_info = tmp_ext;
+ logical_sectors[logical] =
+ ext_info.first_sector + ext_info.offset;
+ read_sector(buffer.c.b, logical_sectors[logical++]);
+ i = 4;
+ do {
+ for (pn = 0;
+ pn < 4 && (!buffer.p.part[pn].sys_ind ||
+ is_extended(buffer.p.part[pn].sys_ind));
+ pn++);
+
+ if (pn < 4) {
+ p = & buffer.p.part[pn];
+ bs = get_start_sect(p);
+ bsz = get_nr_sects(p);
+
+ if (add_part(i++, p->sys_ind, p->boot_ind,
+ logical_sectors[logical-1],
+ logical_sectors[logical-1] + bs + bsz - 1,
+ bs, 1, &errmsg)) {
+ char *bad = _("Bad logical partition");
+ char *msg = (char *) xmalloc(strlen(bad) + strlen(errmsg) + 30);
+ sprintf(msg, "%s %d: %s", bad, i, errmsg);
+ fatal(msg, 4);
+ }
+ }
+
+ for (pn = 0;
+ pn < 4 && !is_extended(buffer.p.part[pn].sys_ind);
+ pn++);
+ if (pn < 4) {
+ p = & buffer.p.part[pn];
+ bs = get_start_sect(p);
+ logical_sectors[logical] = ext_info.first_sector
+ + ext_info.offset + bs;
+ read_sector(buffer.c.b, logical_sectors[logical++]);
+ }
+ } while (pn < 4 && logical < MAXIMUM_PARTS-4);
+ }
+ }
+}
+
+static void
+fill_part_table(struct partition *p, partition_info *pi) {
+ long long begin;
+
+ p->boot_ind = pi->flags;
+ p->sys_ind = pi->id;
+ begin = pi->first_sector + pi->offset;
+ if (IS_LOGICAL(pi->num))
+ set_start_sect(p,pi->offset);
+ else
+ set_start_sect(p,begin);
+ set_nr_sects(p, pi->last_sector - begin + 1);
+ set_hsc_begin(p, begin);
+ set_hsc_end(p, pi->last_sector);
+}
+
+static void
+fill_primary_table(partition_table *buffer) {
+ int i;
+
+ /* Zero out existing table */
+ for (i = 0x1BE; i < SECTOR_SIZE; i++)
+ buffer->c.b[i] = 0;
+
+ for (i = 0; i < num_parts; i++)
+ if (IS_PRIMARY(p_info[i].num))
+ fill_part_table(&(buffer->p.part[p_info[i].num]), &(p_info[i]));
+
+ if (is_extended(ext_info.id))
+ fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info);
+
+ buffer->p.magicflag[0] = PART_TABLE_FLAG0;
+ buffer->p.magicflag[1] = PART_TABLE_FLAG1;
+}
+
+static void
+fill_logical_table(partition_table *buffer, partition_info *pi) {
+ struct partition *p;
+ int i;
+
+ for (i = 0; i < logical && pi->first_sector != logical_sectors[i]; i++);
+ if (i == logical || buffer->p.magicflag[0] != PART_TABLE_FLAG0
+ || buffer->p.magicflag[1] != PART_TABLE_FLAG1)
+ for (i = 0; i < SECTOR_SIZE; i++)
+ buffer->c.b[i] = 0;
+
+ /* Zero out existing table */
+ for (i = 0x1BE; i < SECTOR_SIZE; i++)
+ buffer->c.b[i] = 0;
+
+ fill_part_table(&(buffer->p.part[0]), pi);
+
+ for (i = 0;
+ i < num_parts && pi->num != p_info[i].num - 1;
+ i++);
+
+ if (i < num_parts) {
+ p = &(buffer->p.part[1]);
+ pi = &(p_info[i]);
+
+ p->boot_ind = 0;
+ p->sys_ind = DOS_EXTENDED;
+ set_start_sect(p, pi->first_sector - ext_info.first_sector - ext_info.offset);
+ set_nr_sects(p, pi->last_sector - pi->first_sector + 1);
+ set_hsc_begin(p, pi->first_sector);
+ set_hsc_end(p, pi->last_sector);
+ }
+
+ buffer->p.magicflag[0] = PART_TABLE_FLAG0;
+ buffer->p.magicflag[1] = PART_TABLE_FLAG1;
+}
+
+static void
+write_part_table(void) {
+ int i, ct, done = FALSE, len;
+ partition_table buffer;
+ struct stat s;
+ int is_bdev;
+ char response[LINE_LENGTH];
+
+ if (opentype == O_RDONLY) {
+ print_warning(_("Opened disk read-only - you have no permission to write"));
+ refresh();
+ getch();
+ clear_warning();
+ return;
+ }
+
+ is_bdev = 0;
+ if(fstat(fd, &s) == 0 && S_ISBLK(s.st_mode))
+ is_bdev = 1;
+
+ if (is_bdev) {
+ print_warning(_("Warning!! This may destroy data on your disk!"));
+
+ while (!done) {
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Are you sure you want to write the partition table "
+ "to disk? (yes or no): "));
+ len = get_string(response, LINE_LENGTH, NULL);
+ clear_warning();
+ if (len == GS_ESCAPE)
+ return;
+ else if (strcasecmp(response, _("no")) == 0 ||
+ strcasecmp(response, "no") == 0) {
+ print_warning(_("Did not write partition table to disk"));
+ return;
+ } else if (strcasecmp(response, _("yes")) == 0 ||
+ strcasecmp(response, "yes") == 0)
+ done = TRUE;
+ else
+ print_warning(_("Please enter `yes' or `no'"));
+ }
+
+ clear_warning();
+ print_warning(_("Writing partition table to disk..."));
+ refresh();
+ }
+
+ read_sector(buffer.c.b, 0);
+ fill_primary_table(&buffer);
+ write_sector(buffer.c.b, 0);
+
+ for (i = 0; i < num_parts; i++)
+ if (IS_LOGICAL(p_info[i].num)) {
+ read_sector(buffer.c.b, p_info[i].first_sector);
+ fill_logical_table(&buffer, &(p_info[i]));
+ write_sector(buffer.c.b, p_info[i].first_sector);
+ }
+
+ if (is_bdev) {
+#ifdef BLKRRPART
+ sync();
+ if (!ioctl(fd,BLKRRPART))
+ changed = TRUE;
+#endif
+ sync();
+
+ clear_warning();
+ if (changed)
+ print_warning(_("Wrote partition table to disk"));
+ else
+ print_warning(_("Wrote partition table, but re-read table failed. Run partprobe(8), kpartx(8) or reboot to update table."));
+ } else
+ print_warning(_("Wrote partition table to disk"));
+
+ /* Check: unique bootable primary partition? */
+ ct = 0;
+ for (i = 0; i < num_parts; i++)
+ if (IS_PRIMARY(i) && p_info[i].flags == ACTIVE_FLAG)
+ ct++;
+ if (ct == 0)
+ print_warning(_("No primary partitions are marked bootable. DOS MBR cannot boot this."));
+ if (ct > 1)
+ print_warning(_("More than one primary partition is marked bootable. DOS MBR cannot boot this."));
+}
+
+static void
+fp_printf(FILE *fp, char *format, ...) {
+ va_list args;
+ char buf[1024];
+ int y, x __attribute__((unused));
+
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+
+ if (fp == NULL) {
+ /* The following works best if the string to be printed has at
+ most only one newline. */
+ printw("%s", buf);
+ getyx(stdscr, y, x);
+ if (y >= COMMAND_LINE_Y-2) {
+ menuContinue();
+ erase();
+ move(0, 0);
+ }
+ } else
+ fprintf(fp, "%s", buf);
+}
+
+#define MAX_PER_LINE 16
+static void
+print_file_buffer(FILE *fp, unsigned char *buffer) {
+ int i,l;
+
+ for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) {
+ if (l == 0)
+ fp_printf(fp, "0x%03X:", i);
+ fp_printf(fp, " %02X", buffer[i]);
+ if (l == MAX_PER_LINE - 1) {
+ fp_printf(fp, "\n");
+ l = -1;
+ }
+ }
+ if (l > 0)
+ fp_printf(fp, "\n");
+ fp_printf(fp, "\n");
+}
+
+static void
+print_raw_table(void) {
+ int i, to_file;
+ partition_table buffer;
+ char fname[LINE_LENGTH];
+ FILE *fp;
+
+ if (print_only) {
+ fp = stdout;
+ to_file = TRUE;
+ } else {
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Enter filename or press RETURN to display on screen: "));
+
+ if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
+ return;
+
+ if (to_file) {
+ if ((fp = fopen(fname, "w")) == NULL) {
+ char errstr[LINE_LENGTH];
+ snprintf(errstr, sizeof(errstr),
+ _("cannot open %s"), fname);
+ print_warning(errstr);
+ return;
+ }
+ } else {
+ fp = NULL;
+ erase();
+ move(0, 0);
+ }
+ }
+
+ fp_printf(fp, _("Disk Drive: %s\n"), disk_device);
+
+ fp_printf(fp, _("Sector 0:\n"));
+ read_sector(buffer.c.b, 0);
+ fill_primary_table(&buffer);
+ print_file_buffer(fp, buffer.c.b);
+
+ for (i = 0; i < num_parts; i++)
+ if (IS_LOGICAL(p_info[i].num)) {
+ fp_printf(fp, _("Sector %d:\n"), p_info[i].first_sector);
+ read_sector(buffer.c.b, p_info[i].first_sector);
+ fill_logical_table(&buffer, &(p_info[i]));
+ print_file_buffer(fp, buffer.c.b);
+ }
+
+ if (to_file) {
+ if (!print_only)
+ fclose(fp);
+ } else {
+ menuContinue();
+ }
+}
+
+static void
+print_p_info_entry(FILE *fp, partition_info *p) {
+ long long size;
+ char part_str[40];
+
+ if (p->id == UNUSABLE)
+ fp_printf(fp, _(" None "));
+ else if (p->id == FREE_SPACE && p->num == PRI_OR_LOG)
+ fp_printf(fp, _(" Pri/Log"));
+ else if (p->id == FREE_SPACE && p->num == PRIMARY)
+ fp_printf(fp, _(" Primary"));
+ else if (p->id == FREE_SPACE && p->num == LOGICAL)
+ fp_printf(fp, _(" Logical"));
+ else
+ fp_printf(fp, "%2d %-7.7s", p->num+1,
+ IS_LOGICAL(p->num) ? _("Logical") : _("Primary"));
+
+ fp_printf(fp, " ");
+
+ fp_printf(fp, "%11lld%c", p->first_sector,
+ ((p->first_sector/cylinder_size) !=
+ ((float)p->first_sector/cylinder_size) ?
+ '*' : ' '));
+
+ fp_printf(fp, "%11lld%c", p->last_sector,
+ (((p->last_sector+1)/cylinder_size) !=
+ ((float)(p->last_sector+1)/cylinder_size) ?
+ '*' : ' '));
+
+ fp_printf(fp, "%6ld%c", p->offset,
+ ((((p->first_sector == 0 || IS_LOGICAL(p->num)) &&
+ (p->offset != sectors)) ||
+ (p->first_sector != 0 && IS_PRIMARY(p->num) &&
+ p->offset != 0)) ?
+ '#' : ' '));
+
+ size = p->last_sector - p->first_sector + 1;
+ fp_printf(fp, "%11lld%c", size,
+ ((size/cylinder_size) != ((float)size/cylinder_size) ?
+ '*' : ' '));
+
+ /* fp_printf(fp, " "); */
+
+ if (p->id == UNUSABLE)
+ sprintf(part_str, "%.15s", _("Unusable"));
+ else if (p->id == FREE_SPACE)
+ sprintf(part_str, "%.15s", _("Free Space"));
+ else if (partition_type_name(p->id))
+ sprintf(part_str, "%.15s (%02X)", _(partition_type_name(p->id)), p->id);
+ else
+ sprintf(part_str, "%.15s (%02X)", _("Unknown"), p->id);
+ fp_printf(fp, "%-20.20s", part_str);
+
+ fp_printf(fp, " ");
+
+ if (p->flags == ACTIVE_FLAG)
+ fp_printf(fp, _("Boot"), p->flags);
+ else if (p->flags != 0)
+ fp_printf(fp, _("(%02X)"), p->flags);
+ else
+ fp_printf(fp, _("None"), p->flags);
+
+ fp_printf(fp, "\n");
+}
+
+static void
+print_p_info(void) {
+ char fname[LINE_LENGTH];
+ FILE *fp;
+ int i, to_file, pext = is_extended(ext_info.id);
+
+ if (print_only) {
+ fp = stdout;
+ to_file = TRUE;
+ } else {
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Enter filename or press RETURN to display on screen: "));
+
+ if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
+ return;
+
+ if (to_file) {
+ if ((fp = fopen(fname, "w")) == NULL) {
+ char errstr[LINE_LENGTH];
+ snprintf(errstr, LINE_LENGTH, _("cannot open %s"), fname);
+ print_warning(errstr);
+ return;
+ }
+ } else {
+ fp = NULL;
+ erase();
+ move(0, 0);
+ }
+ }
+
+ fp_printf(fp, _("Partition Table for %s\n"), disk_device);
+ fp_printf(fp, "\n");
+ fp_printf(fp, _(" First Last\n"));
+ fp_printf(fp, _(" # Type Sector Sector Offset Length Filesystem Type (ID) Flag\n"));
+ fp_printf(fp, _("-- ------- ----------- ----------- ------ ----------- -------------------- ----\n"));
+
+ for (i = 0; i < num_parts; i++) {
+ if (pext && (p_info[i].first_sector >= ext_info.first_sector)) {
+ print_p_info_entry(fp,&ext_info);
+ pext = FALSE;
+ }
+ print_p_info_entry(fp, &(p_info[i]));
+ }
+
+ if (to_file) {
+ if (!print_only)
+ fclose(fp);
+ } else {
+ menuContinue();
+ }
+}
+
+static void
+print_part_entry(FILE *fp, int num, partition_info *pi) {
+ long long first = 0, start = 0, end = 0, size = 0;
+ unsigned char ss, es, sh, eh;
+ int sc, ec;
+ int flags = 0, id = 0;
+
+ ss = sh = es = eh = 0;
+ sc = ec = 0;
+
+ if (pi != NULL) {
+ flags = pi->flags;
+ id = pi->id;
+
+ if (IS_LOGICAL(num))
+ first = pi->offset;
+ else
+ first = pi->first_sector + pi->offset;
+
+ start = pi->first_sector + pi->offset;
+ end = pi->last_sector;
+ size = end - start + 1;
+
+ set_hsc0(&sh, &ss, &sc, start);
+ set_hsc0(&eh, &es, &ec, end);
+ }
+
+ fp_printf(fp, "%2d 0x%02X %4d %4d %5d 0x%02X %4d %4d %5d %11lld %11lld\n",
+ num+1, flags, sh, ss, sc, id, eh, es, ec, first, size);
+}
+
+
+static void
+print_part_table(void) {
+ int i, j, to_file;
+ char fname[LINE_LENGTH];
+ FILE *fp;
+
+ if (print_only) {
+ fp = stdout;
+ to_file = TRUE;
+ } else {
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Enter filename or press RETURN to display on screen: "));
+
+ if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
+ return;
+
+ if (to_file) {
+ if ((fp = fopen(fname, "w")) == NULL) {
+ char errstr[LINE_LENGTH];
+ snprintf(errstr, LINE_LENGTH, _("cannot open %s"), fname);
+ print_warning(errstr);
+ return;
+ }
+ } else {
+ fp = NULL;
+ erase();
+ move(0, 0);
+ }
+ }
+
+ fp_printf(fp, _("Partition Table for %s\n"), disk_device);
+ fp_printf(fp, "\n");
+ /* Three-line heading. Read "Start Sector" etc vertically. */
+ fp_printf(fp, _(" ---Starting---- ----Ending----- Start Number of\n"));
+ fp_printf(fp, _(" # Flags Head Sect Cyl ID Head Sect Cyl Sector Sectors\n"));
+ fp_printf(fp, _("-- ----- ---- ---- ----- ---- ---- ---- ----- ----------- -----------\n"));
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0;
+ j < num_parts && (p_info[j].id <= 0 || p_info[j].num != i);
+ j++);
+ if (j < num_parts) {
+ print_part_entry(fp, i, &(p_info[j]));
+ } else if (is_extended(ext_info.id) && ext_info.num == i) {
+ print_part_entry(fp, i, &ext_info);
+ } else {
+ print_part_entry(fp, i, NULL);
+ }
+ }
+
+ for (i = 0; i < num_parts; i++)
+ if (IS_LOGICAL(p_info[i].num))
+ print_part_entry(fp, p_info[i].num, &(p_info[i]));
+
+ if (to_file) {
+ if (!print_only)
+ fclose(fp);
+ } else {
+ menuContinue();
+ }
+}
+
+static void
+print_tables(void) {
+ int done = FALSE;
+
+ static struct MenuItem menuFormat[]=
+ {
+ { 'r', N_("Raw"), N_("Print the table using raw data format") },
+ { 's', N_("Sectors"), N_("Print the table ordered by sectors") },
+ { 't', N_("Table"), N_("Just print the partition table") },
+ { ESC, N_("Cancel"), N_("Don't print the table") },
+ { 0, NULL, NULL }
+ };
+
+ while (!done)
+ switch ( toupper(menuSimple( menuFormat, 2)) ) {
+ case 'R':
+ print_raw_table();
+ done = TRUE;
+ break;
+ case 'S':
+ print_p_info();
+ done = TRUE;
+ break;
+ case 'T':
+ print_part_table();
+ done = TRUE;
+ break;
+ case ESC:
+ done = TRUE;
+ break;
+ }
+}
+
+#define END_OF_HELP "EOHS!"
+static void
+display_help(void) {
+ char *help_text[] = {
+ N_("Help Screen for cfdisk"),
+ "",
+ N_("This is cfdisk, a curses based disk partitioning program, which"),
+ N_("allows you to create, delete and modify partitions on your hard"),
+ N_("disk drive."),
+ "",
+ N_("Copyright (C) 1994-1999 Kevin E. Martin & aeb"),
+ "",
+ N_("Command Meaning"),
+ N_("------- -------"),
+ N_(" b Toggle bootable flag of the current partition"),
+ N_(" d Delete the current partition"),
+ N_(" g Change cylinders, heads, sectors-per-track parameters"),
+ N_(" WARNING: This option should only be used by people who"),
+ N_(" know what they are doing."),
+ N_(" h Print this screen"),
+ N_(" m Maximize disk usage of the current partition"),
+ N_(" Note: This may make the partition incompatible with"),
+ N_(" DOS, OS/2, ..."),
+ N_(" n Create new partition from free space"),
+ N_(" p Print partition table to the screen or to a file"),
+ N_(" There are several different formats for the partition"),
+ N_(" that you can choose from:"),
+ N_(" r - Raw data (exactly what would be written to disk)"),
+ N_(" s - Table ordered by sectors"),
+ N_(" t - Table in raw format"),
+ N_(" q Quit program without writing partition table"),
+ N_(" t Change the filesystem type"),
+ N_(" u Change units of the partition size display"),
+ N_(" Rotates through MB, sectors and cylinders"),
+ N_(" W Write partition table to disk (must enter upper case W)"),
+ N_(" Since this might destroy data on the disk, you must"),
+ N_(" either confirm or deny the write by entering `yes' or"),
+ N_(" `no'"),
+ N_("Up Arrow Move cursor to the previous partition"),
+ N_("Down Arrow Move cursor to the next partition"),
+ N_("CTRL-L Redraws the screen"),
+ N_(" ? Print this screen"),
+ "",
+ N_("Note: All of the commands can be entered with either upper or lower"),
+ N_("case letters (except for Writes)."),
+ END_OF_HELP
+ };
+
+ int cur_line = 0;
+ FILE *fp = NULL;
+
+ erase();
+ move(0, 0);
+ while (strcmp(help_text[cur_line], END_OF_HELP)) {
+ if (help_text[cur_line][0])
+ fp_printf(fp, "%s\n", _(help_text[cur_line]));
+ else
+ fp_printf(fp, "\n");
+ cur_line++;
+ }
+ menuContinue();
+}
+
+static int
+change_geometry(void) {
+ int ret_val = FALSE;
+ int done = FALSE;
+ char def[LINE_LENGTH];
+ char response[LINE_LENGTH];
+ long long tmp_val;
+ int i;
+
+ while (!done) {
+ static struct MenuItem menuGeometry[]=
+ {
+ { 'c', N_("Cylinders"), N_("Change cylinder geometry") },
+ { 'h', N_("Heads"), N_("Change head geometry") },
+ { 's', N_("Sectors"), N_("Change sector geometry") },
+ { 'd', N_("Done"), N_("Done with changing geometry") },
+ { 0, NULL, NULL }
+ };
+ move(COMMAND_LINE_Y, COMMAND_LINE_X);
+ clrtoeol();
+ refresh();
+
+ clear_warning();
+
+ switch (toupper( menuSimple(menuGeometry, 3) )) {
+ case 'C':
+ sprintf(def, "%llu", actual_size/cylinder_size);
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Enter the number of cylinders: "));
+ i = get_string(response, LINE_LENGTH, def);
+ if (i == GS_DEFAULT) {
+ user_cylinders = actual_size/cylinder_size;
+ ret_val = TRUE;
+ } else if (i > 0) {
+ tmp_val = atoll(response);
+ if (tmp_val > 0) {
+ user_cylinders = tmp_val;
+ ret_val = TRUE;
+ } else
+ print_warning(_("Illegal cylinders value"));
+ }
+ break;
+ case 'H':
+ sprintf(def, "%d", heads);
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Enter the number of heads: "));
+ if (get_string(response, LINE_LENGTH, def) > 0) {
+ tmp_val = atoll(response);
+ if (tmp_val > 0 && tmp_val <= MAX_HEADS) {
+ user_heads = tmp_val;
+ ret_val = TRUE;
+ } else
+ print_warning(_("Illegal heads value"));
+ }
+ break;
+ case 'S':
+ sprintf(def, "%d", sectors);
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+ _("Enter the number of sectors per track: "));
+ if (get_string(response, LINE_LENGTH, def) > 0) {
+ tmp_val = atoll(response);
+ if (tmp_val > 0 && tmp_val <= MAX_SECTORS) {
+ user_sectors = tmp_val;
+ ret_val = TRUE;
+ } else
+ print_warning(_("Illegal sectors value"));
+ }
+ break;
+ case ESC:
+ case 'D':
+ done = TRUE;
+ break;
+ default:
+ putchar(BELL);
+ break;
+ }
+
+ if (ret_val) {
+ decide_on_geometry();
+ draw_screen();
+ }
+ }
+
+ if (ret_val) {
+ long long disk_end;
+
+ disk_end = actual_size-1;
+
+ if (p_info[num_parts-1].last_sector > disk_end) {
+ while (p_info[num_parts-1].first_sector > disk_end) {
+ if (p_info[num_parts-1].id == FREE_SPACE ||
+ p_info[num_parts-1].id == UNUSABLE)
+ remove_part(num_parts-1);
+ else
+ del_part(num_parts-1);
+ }
+
+ p_info[num_parts-1].last_sector = disk_end;
+
+ if (ext_info.last_sector > disk_end)
+ ext_info.last_sector = disk_end;
+ } else if (p_info[num_parts-1].last_sector < disk_end) {
+ if (p_info[num_parts-1].id == FREE_SPACE ||
+ p_info[num_parts-1].id == UNUSABLE) {
+ p_info[num_parts-1].last_sector = disk_end;
+ } else {
+ insert_empty_part(num_parts,
+ p_info[num_parts-1].last_sector+1,
+ disk_end);
+ }
+ }
+
+ /* Make sure the partitions are correct */
+ check_part_info();
+ }
+
+ return ret_val;
+}
+
+static void
+change_id(int i) {
+ char id[LINE_LENGTH], def[LINE_LENGTH];
+ int num_types = 0;
+ int num_across, num_down;
+ int len, new_id = ((p_info[i].id == LINUX) ? LINUX_SWAP : LINUX);
+ int y_start, y_end, row, row_min, row_max, row_offset, j, needmore;
+
+ for (j = 1; i386_sys_types[j].name; j++) ;
+ num_types = j-1; /* do not count the Empty type */
+
+ num_across = COLS/COL_ID_WIDTH;
+ num_down = (((float)num_types)/num_across + 1);
+ y_start = COMMAND_LINE_Y - 1 - num_down;
+ if (y_start < 1) {
+ y_start = 1;
+ y_end = COMMAND_LINE_Y - 2;
+ } else {
+ if (y_start > DISK_TABLE_START+cur_part+4)
+ y_start = DISK_TABLE_START+cur_part+4;
+ y_end = y_start + num_down - 1;
+ }
+
+ row_min = 1;
+ row_max = COMMAND_LINE_Y - 2;
+ row_offset = 0;
+ do {
+ for (j = y_start - 1; j <= y_end + 1; j++) {
+ move(j, 0);
+ clrtoeol();
+ }
+ needmore = 0;
+ for (j = 1; i386_sys_types[j].name; j++) {
+ row = y_start + (j-1) % num_down - row_offset;
+ if (row >= row_min && row <= row_max) {
+ move(row, ((j-1)/num_down)*COL_ID_WIDTH + 1);
+ printw("%02X %-20.20s",
+ i386_sys_types[j].type,
+ _(i386_sys_types[j].name));
+ }
+ if (row > row_max)
+ needmore = 1;
+ }
+ if (needmore)
+ menuContinue();
+ row_offset += (row_max - row_min + 1);
+ } while(needmore);
+
+ sprintf(def, "%02X", new_id);
+ mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, _("Enter filesystem type: "));
+ if ((len = get_string(id, 3, def)) <= 0 && len != GS_DEFAULT)
+ return;
+
+ if (len != GS_DEFAULT) {
+ if (!isxdigit(id[0]))
+ return;
+ new_id = (isdigit(id[0]) ? id[0] - '0' : tolower(id[0]) - 'a' + 10);
+ if (len == 2) {
+ if (isxdigit(id[1]))
+ new_id = new_id*16 +
+ (isdigit(id[1]) ? id[1] - '0' : tolower(id[1]) - 'a' + 10);
+ else
+ return;
+ }
+ }
+
+ if (new_id == 0)
+ print_warning(_("Cannot change FS Type to empty"));
+ else if (is_extended(new_id))
+ print_warning(_("Cannot change FS Type to extended"));
+ else
+ p_info[i].id = new_id;
+}
+
+static void
+draw_partition(int i) {
+ int j;
+ int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
+ char *t;
+ long long size;
+ double fsize;
+
+ if (!arrow_cursor) {
+ move(y, 0);
+ for (j = 0; j < COLS; j++)
+ addch(' ');
+ }
+
+ if (p_info[i].id > 0) {
+ char *dbn = my_basename(disk_device);
+ int l = strlen(dbn);
+ int digit_last = isdigit(dbn[l-1]);
+
+ mvprintw(y, NAME_START,
+ "%s%s%d", dbn, (digit_last ? "p" : ""),
+ p_info[i].num+1);
+ if (p_info[i].flags) {
+ if (p_info[i].flags == ACTIVE_FLAG)
+ mvaddstr(y, FLAGS_START, _("Boot"));
+ else
+ mvprintw(y, FLAGS_START, _("Unk(%02X)"), p_info[i].flags);
+ if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
+ if (p_info[i].offset != sectors)
+ addstr(_(", NC"));
+ } else {
+ if (p_info[i].offset != 0)
+ addstr(_(", NC"));
+ }
+ } else {
+ if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
+ if (p_info[i].offset != sectors)
+ mvaddstr(y, FLAGS_START, _("NC"));
+ } else {
+ if (p_info[i].offset != 0)
+ mvaddstr(y, FLAGS_START, _("NC"));
+ }
+ }
+ }
+ mvaddstr(y, PTYPE_START,
+ (p_info[i].id == UNUSABLE ? "" :
+ (IS_LOGICAL(p_info[i].num) ? _("Logical") :
+ (p_info[i].num >= 0 ? _("Primary") :
+ (p_info[i].num == PRI_OR_LOG ? _("Pri/Log") :
+ (p_info[i].num == PRIMARY ? _("Primary") : _("Logical")))))));
+
+ t = partition_type_text(i);
+ if (t)
+ mvaddstr(y, FSTYPE_START, t);
+ else
+ mvprintw(y, FSTYPE_START, _("Unknown (%02X)"), p_info[i].id);
+
+ if (p_info[i].volume_label[0]) {
+ int l = strlen(p_info[i].volume_label);
+ int s = SIZE_START-5-l;
+ mvprintw(y, (s > LABEL_START) ? LABEL_START : s,
+ " [%s] ", p_info[i].volume_label);
+ }
+
+ size = p_info[i].last_sector - p_info[i].first_sector + 1;
+ fsize = (double) size * SECTOR_SIZE;
+ if (display_units == SECTORS)
+ mvprintw(y, SIZE_START, "%11lld", size);
+ else if (display_units == CYLINDERS)
+ mvprintw(y, SIZE_START, "%11lld", size/cylinder_size);
+ else if (display_units == MEGABYTES)
+ mvprintw(y, SIZE_START, "%11.2f", ceiling((100*fsize)/(K*K))/100);
+ else if (display_units == GIGABYTES)
+ mvprintw(y, SIZE_START, "%11.2f", ceiling((100*fsize)/(K*K*K))/100);
+ if (size % cylinder_size != 0 ||
+ p_info[i].first_sector % cylinder_size != 0)
+ mvprintw(y, COLUMNS-1, "*");
+}
+
+static void
+init_const(void) {
+ if (!defined) {
+ NAME_START = (((float)NAME_START)/COLUMNS)*COLS;
+ FLAGS_START = (((float)FLAGS_START)/COLUMNS)*COLS;
+ PTYPE_START = (((float)PTYPE_START)/COLUMNS)*COLS;
+ FSTYPE_START = (((float)FSTYPE_START)/COLUMNS)*COLS;
+ LABEL_START = (((float)LABEL_START)/COLUMNS)*COLS;
+ SIZE_START = (((float)SIZE_START)/COLUMNS)*COLS;
+ COMMAND_LINE_X = (((float)COMMAND_LINE_X)/COLUMNS)*COLS;
+
+ COMMAND_LINE_Y = LINES - 4;
+ WARNING_START = LINES - 2;
+
+ if ((NUM_ON_SCREEN = COMMAND_LINE_Y - DISK_TABLE_START - 3) <= 0)
+ NUM_ON_SCREEN = 1;
+
+ COLUMNS = COLS;
+ defined = TRUE;
+ }
+}
+
+static void
+draw_screen(void) {
+ int i;
+ char *line;
+
+ line = (char *) xmalloc((COLS+1)*sizeof(char));
+
+ if (warning_last_time) {
+ for (i = 0; i < COLS; i++) {
+ move(WARNING_START, i);
+ line[i] = inch();
+ }
+ line[COLS] = 0;
+ }
+
+ erase();
+
+ if (warning_last_time)
+ mvaddstr(WARNING_START, 0, line);
+
+
+ snprintf(line, COLS+1, "cfdisk (%s)", PACKAGE_STRING);
+ mvaddstr(HEADER_START, (COLS-strlen(line))/2, line);
+ snprintf(line, COLS+1, _("Disk Drive: %s"), disk_device);
+ mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line);
+ {
+ long long bytes = actual_size*(long long) SECTOR_SIZE;
+ long long megabytes = bytes/(K*K);
+
+ if (megabytes < 10000)
+ sprintf(line, _("Size: %lld bytes, %lld MB"),
+ bytes, megabytes);
+ else
+ sprintf(line, _("Size: %lld bytes, %lld.%lld GB"),
+ bytes, megabytes/K, (10*megabytes/K)%10);
+ }
+ mvaddstr(HEADER_START+3, (COLS-strlen(line))/2, line);
+ snprintf(line, COLS+1, _("Heads: %d Sectors per Track: %d Cylinders: %lld"),
+ heads, sectors, cylinders);
+ mvaddstr(HEADER_START+4, (COLS-strlen(line))/2, line);
+
+ mvaddstr(DISK_TABLE_START, NAME_START, _("Name"));
+ mvaddstr(DISK_TABLE_START, FLAGS_START, _("Flags"));
+ mvaddstr(DISK_TABLE_START, PTYPE_START-1, _("Part Type"));
+ mvaddstr(DISK_TABLE_START, FSTYPE_START, _("FS Type"));
+ mvaddstr(DISK_TABLE_START, LABEL_START+1, _("[Label]"));
+ if (display_units == SECTORS)
+ mvaddstr(DISK_TABLE_START, SIZE_START, _(" Sectors"));
+ else if (display_units == CYLINDERS)
+ mvaddstr(DISK_TABLE_START, SIZE_START, _(" Cylinders"));
+ else if (display_units == MEGABYTES)
+ mvaddstr(DISK_TABLE_START, SIZE_START, _(" Size (MB)"));
+ else if (display_units == GIGABYTES)
+ mvaddstr(DISK_TABLE_START, SIZE_START, _(" Size (GB)"));
+
+ move(DISK_TABLE_START+1, 1);
+ for (i = 1; i < COLS-1; i++)
+ addch('-');
+
+ if (NUM_ON_SCREEN >= num_parts)
+ for (i = 0; i < num_parts; i++)
+ draw_partition(i);
+ else
+ for (i = (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
+ i < NUM_ON_SCREEN + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN &&
+ i < num_parts;
+ i++)
+ draw_partition(i);
+
+ free(line);
+}
+
+static void
+draw_cursor(int move) {
+ if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts)) {
+ print_warning(_("No more partitions"));
+ return;
+ }
+
+ if (arrow_cursor)
+ mvaddstr(DISK_TABLE_START + cur_part + 2
+ - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, " ");
+ else
+ draw_partition(cur_part);
+
+ cur_part += move;
+
+ if (((cur_part - move)/NUM_ON_SCREEN)*NUM_ON_SCREEN !=
+ (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN)
+ draw_screen();
+
+ if (arrow_cursor)
+ mvaddstr(DISK_TABLE_START + cur_part + 2
+ - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "-->");
+ else {
+ standout();
+ draw_partition(cur_part);
+ standend();
+ }
+}
+
+static void
+do_curses_fdisk(void) {
+ int done = FALSE;
+ int command;
+ int is_first_run = TRUE;
+
+ static struct MenuItem menuMain[] = {
+ { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
+ { 'd', N_("Delete"), N_("Delete the current partition") },
+ { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
+ { 'h', N_("Help"), N_("Print help screen") },
+ { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
+ { 'n', N_("New"), N_("Create new partition from free space") },
+ { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
+ { 'q', N_("Quit"), N_("Quit program without writing partition table") },
+ { 't', N_("Type"), N_("Change the filesystem type (DOS, Linux, OS/2 and so on)") },
+ { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
+ { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
+ { 0, NULL, NULL }
+ };
+ curses_started = 1;
+ initscr();
+ init_const();
+
+ old_SIGINT = signal(SIGINT, die);
+ old_SIGTERM = signal(SIGTERM, die);
+#ifdef DEBUG
+ signal(SIGINT, old_SIGINT);
+ signal(SIGTERM, old_SIGTERM);
+#endif
+
+ cbreak();
+ noecho();
+ nonl();
+
+ fill_p_info();
+
+ draw_screen();
+
+ while (!done) {
+ char *s;
+
+ draw_cursor(0);
+
+ if (p_info[cur_part].id == FREE_SPACE) {
+ s = ((opentype == O_RDWR) ? "hnpquW" : "hnpqu");
+ command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
+ s, MENU_BUTTON | MENU_ACCEPT_OTHERS, 5);
+ } else if (p_info[cur_part].id > 0) {
+ s = ((opentype == O_RDWR) ? "bdhmpqtuW" : "bdhmpqtu");
+ command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
+ s, MENU_BUTTON | MENU_ACCEPT_OTHERS, is_first_run ? 7 : 0);
+ } else {
+ s = ((opentype == O_RDWR) ? "hpquW" : "hpqu");
+ command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
+ s, MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
+ }
+ is_first_run = FALSE;
+ switch ( command ) {
+ case 'B':
+ case 'b':
+ if (p_info[cur_part].id > 0)
+ p_info[cur_part].flags ^= 0x80;
+ else
+ print_warning(_("Cannot make this partition bootable"));
+ break;
+ case 'D':
+ case 'd':
+ if (p_info[cur_part].id > 0) {
+ del_part(cur_part);
+ if (cur_part >= num_parts)
+ cur_part = num_parts - 1;
+ draw_screen();
+ } else
+ print_warning(_("Cannot delete an empty partition"));
+ break;
+ case 'G':
+ case 'g':
+ if (change_geometry())
+ draw_screen();
+ break;
+ case 'M':
+ case 'm':
+ if (p_info[cur_part].id > 0) {
+ if (p_info[cur_part].first_sector == 0 ||
+ IS_LOGICAL(p_info[cur_part].num)) {
+ if (p_info[cur_part].offset == sectors)
+ p_info[cur_part].offset = 1;
+ else
+ p_info[cur_part].offset = sectors;
+ draw_screen();
+ } else if (p_info[cur_part].offset != 0)
+ p_info[cur_part].offset = 0;
+ else
+ print_warning(_("Cannot maximize this partition"));
+ } else
+ print_warning(_("Cannot maximize this partition"));
+ break;
+ case 'N':
+ case 'n':
+ if (p_info[cur_part].id == FREE_SPACE) {
+ new_part(cur_part);
+ draw_screen();
+ } else if (p_info[cur_part].id == UNUSABLE)
+ print_warning(_("This partition is unusable"));
+ else
+ print_warning(_("This partition is already in use"));
+ break;
+ case 'P':
+ case 'p':
+ print_tables();
+ draw_screen();
+ break;
+ case 'Q':
+ case 'q':
+ done = TRUE;
+ break;
+ case 'T':
+ case 't':
+ if (p_info[cur_part].id > 0) {
+ change_id(cur_part);
+ draw_screen();
+ } else
+ print_warning(_("Cannot change the type of an empty partition"));
+ break;
+ case 'U':
+ case 'u':
+ if (display_units == GIGABYTES)
+ display_units = MEGABYTES;
+ else if (display_units == MEGABYTES)
+ display_units = SECTORS;
+ else if (display_units == SECTORS)
+ display_units = CYLINDERS;
+ else if (display_units == CYLINDERS)
+ display_units = MEGABYTES; /* not yet GIGA */
+ draw_screen();
+ break;
+ case 'W':
+ write_part_table();
+ break;
+ case 'H':
+ case 'h':
+ case '?':
+ display_help();
+ draw_screen();
+ break;
+ case KEY_UP: /* Up arrow key */
+ case '\020': /* ^P */
+ case 'k': /* Vi-like alternative */
+ draw_cursor(-1);
+ break;
+ case KEY_DOWN: /* Down arrow key */
+ case '\016': /* ^N */
+ case 'j': /* Vi-like alternative */
+ draw_cursor(1);
+ break;
+ case REDRAWKEY:
+ clear();
+ draw_screen();
+ break;
+ case KEY_HOME:
+ draw_cursor(-cur_part);
+ break;
+ case KEY_END:
+ draw_cursor(num_parts - cur_part - 1);
+ break;
+ default:
+ print_warning(_("Illegal command"));
+ putchar(BELL); /* CTRL-G */
+ }
+ }
+
+ die_x(0);
+}
+
+static void
+copyright(void) {
+ fprintf(stderr, _("Copyright (C) 1994-2002 Kevin E. Martin & aeb\n"));
+}
+
+static void
+usage(char *prog_name) {
+ /* Unfortunately, xgettext does not handle multi-line strings */
+ /* so, let's use explicit \n's instead */
+ fprintf(stderr, _("\n"
+"Usage:\n"
+"Print version:\n"
+" %s -v\n"
+"Print partition table:\n"
+" %s -P {r|s|t} [options] device\n"
+"Interactive use:\n"
+" %s [options] device\n"
+"\n"
+"Options:\n"
+"-a: Use arrow instead of highlighting;\n"
+"-z: Start with a zero partition table, instead of reading the pt from disk;\n"
+"-c C -h H -s S: Override the kernel's idea of the number of cylinders,\n"
+" the number of heads and the number of sectors/track.\n\n"),
+ prog_name, prog_name, prog_name);
+
+ copyright();
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int i, len;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt(argc, argv, "ac:gh:s:vzP:")) != -1)
+ switch (c) {
+ case 'a':
+ arrow_cursor = TRUE;
+ break;
+ case 'c':
+ user_cylinders = cylinders = strtos64_or_err(optarg, _("cannot parse number of cylinders"));
+ if (cylinders <= 0) {
+ fprintf(stderr, "%s: %s\n", argv[0], _("Illegal cylinders value"));
+ exit(1);
+ }
+ break;
+ case 'g':
+ use_partition_table_geometry = TRUE;
+ break;
+ case 'h':
+ user_heads = heads = strtol_or_err(optarg, _("cannot parse number of heads"));
+ if (heads <= 0 || heads > MAX_HEADS) {
+ fprintf(stderr, "%s: %s\n", argv[0], _("Illegal heads value"));
+ exit(1);
+ }
+ break;
+ case 's':
+ user_sectors = sectors = strtol_or_err(optarg, _("cannot parse number of sectors"));
+ if (sectors <= 0 || sectors > MAX_SECTORS) {
+ fprintf(stderr, "%s: %s\n", argv[0], _("Illegal sectors value"));
+ exit(1);
+ }
+ break;
+ case 'v':
+ fprintf(stderr, "cfdisk (%s)\n", PACKAGE_STRING);
+ copyright();
+ exit(0);
+ case 'z':
+ zero_table = TRUE;
+ break;
+ case 'P':
+ len = strlen(optarg);
+ for (i = 0; i < len; i++) {
+ switch (optarg[i]) {
+ case 'r':
+ print_only |= PRINT_RAW_TABLE;
+ break;
+ case 's':
+ print_only |= PRINT_SECTOR_TABLE;
+ break;
+ case 't':
+ print_only |= PRINT_PARTITION_TABLE;
+ break;
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+ break;
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+
+ if (argc-optind == 1)
+ disk_device = argv[optind];
+ else if (argc-optind != 0) {
+ usage(argv[0]);
+ exit(1);
+ } else if ((fd = open(DEFAULT_DEVICE, O_RDONLY)) < 0)
+ disk_device = ALTERNATE_DEVICE;
+ else close(fd);
+
+ if (print_only) {
+ fill_p_info();
+ if (print_only & PRINT_RAW_TABLE)
+ print_raw_table();
+ if (print_only & PRINT_SECTOR_TABLE)
+ print_p_info();
+ if (print_only & PRINT_PARTITION_TABLE)
+ print_part_table();
+ } else
+ do_curses_fdisk();
+
+ return 0;
+}
diff --git a/fdisks/common.h b/fdisks/common.h
new file mode 100644
index 0000000..352b9a5
--- /dev/null
+++ b/fdisks/common.h
@@ -0,0 +1,15 @@
+#ifndef FDISK_COMMON_H
+#define FDISK_COMMON_H
+
+/* common stuff for fdisk, cfdisk, sfdisk */
+
+struct systypes {
+ unsigned char type;
+ char *name;
+};
+
+extern struct systypes i386_sys_types[];
+
+extern char *partname(char *dev, int pno, int lth);
+
+#endif /* FDISK_COMMON_H */
diff --git a/fdisks/fdisk.8 b/fdisks/fdisk.8
new file mode 100644
index 0000000..3c53473
--- /dev/null
+++ b/fdisks/fdisk.8
@@ -0,0 +1,288 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl)
+.\" Copyright 2012 Davidlohr Bueso <dave@gnu.org>
+.\" May be distributed under the GNU General Public License
+.TH FDISK 8 "June 2012" "util-linux" "System Administration"
+.SH NAME
+fdisk \- manipulate disk partition table
+.SH SYNOPSIS
+.B fdisk
+.RB [ \-uc ]
+.RB [ \-b
+.IR sectorsize ]
+.RB [ \-C
+.IR cyls ]
+.RB [ \-H
+.IR heads ]
+.RB [ \-S
+.IR sects ]
+.I device
+.sp
+.B fdisk \-l
+.RB [ \-u ]
+.RI [ device ...]
+.sp
+.B fdisk \-s
+.IR partition ...
+.sp
+.B fdisk \-v
+.sp
+.B fdisk \-h
+.SH DESCRIPTION
+.B fdisk
+(in the first form of invocation)
+is a menu-driven program for creation and manipulation of
+partition tables.
+It understands DOS-type partition tables and BSD- or SUN-type disklabels.
+
+.B fdisk
+does not understand GUID partition tables (GPTs) and it is not designed
+for large partitions. In these cases, use the more advanced GNU
+.BR parted (8).
+
+.B fdisk
+does not use DOS-compatible mode and cylinders as display units by default.
+The old deprecated DOS behavior can be enabled with
+the '-c=dos -u=cylinders' command-line options.
+
+Hard disks can be divided into one or more logical disks called
+.IR partitions .
+This division is recorded in the
+.IR "partition table" ,
+found in sector 0 of the disk.
+(In the BSD world one talks about `disk slices' and a `disklabel'.)
+
+Linux needs at least one partition, namely for its root file system.
+It can use swap files and/or swap partitions, but the latter are more
+efficient. So, usually one will want a second Linux partition
+dedicated as swap partition.
+On Intel-compatible hardware, the BIOS that boots the system
+can often only access the first 1024 cylinders of the disk.
+For this reason people with large disks often create a third partition,
+just a few MB large, typically mounted on
+.IR /boot ,
+to store the kernel image and a few auxiliary files needed at boot time,
+so as to make sure that this stuff is accessible to the BIOS.
+There may be reasons of security, ease of administration and backup,
+or testing, to use more than the minimum number of partitions.
+
+.SH DEVICES
+The
+.I device
+is usually /dev/sda, /dev/sdb or so. A device name refers to the entire disk.
+Old systems without libata (a library used inside the Linux kernel to
+support ATA host controllers and devices) make a difference between IDE and
+SCSI disks. In such cases the device name will be /dev/hd* (IDE) or /dev/sd*
+(SCSI).
+
+The
+.I partition
+is a device name followed by a partition number. For example, /dev/sda1
+is the first partition on the first hard disk in the system.
+See also Linux kernel documentation (the Documentation/devices.txt file).
+
+.SH DISK LABELS
+A BSD/SUN-type disklabel can describe 8 partitions,
+the third of which should be a `whole disk' partition.
+Do not start a partition that actually uses its first sector
+(like a swap partition) at cylinder 0, since that will
+destroy the disklabel.
+
+An IRIX/SGI-type disklabel can describe 16 partitions,
+the eleventh of which should be an entire `volume' partition,
+while the ninth should be labeled `volume header'.
+The volume header will also cover the partition table, i.e.,
+it starts at block zero and extends by default over five cylinders.
+The remaining space in the volume header may be used by header
+directory entries. No partitions may overlap with the volume header.
+Also do not change its type or make some filesystem on it, since
+you will lose the partition table. Use this type of label only when
+working with Linux on IRIX/SGI machines or IRIX/SGI disks under Linux.
+
+A DOS-type partition table can describe an unlimited number
+of partitions. In sector 0 there is room for the description
+of 4 partitions (called `primary'). One of these may be an
+extended partition; this is a box holding logical partitions,
+with descriptors found in a linked list of sectors, each
+preceding the corresponding logical partitions.
+The four primary partitions, present or not, get numbers 1-4.
+Logical partitions start numbering from 5.
+
+In a DOS-type partition table the starting offset and the size
+of each partition is stored in two ways: as an absolute number
+of sectors (given in 32 bits), and as a Cylinders/Heads/Sectors
+triple (given in 10+8+6 bits). The former is OK -- with 512-byte
+sectors this will work up to 2 TB. The latter has two
+problems. First, these C/H/S fields can be filled only
+when the number of heads and the number of sectors per track
+are known. And second, even if we know what these numbers should be,
+the 24 bits that are available do not suffice.
+DOS uses C/H/S only, Windows uses both, Linux never uses C/H/S.
+
+If possible,
+.B fdisk
+will obtain the disk geometry automatically. This is not
+necessarily the physical disk geometry (indeed, modern disks do not
+really have anything like a physical geometry, certainly not something
+that can be described in simplistic Cylinders/Heads/Sectors form),
+but it is the disk geometry that MS-DOS uses for the partition table.
+
+Usually all goes well by default, and there are no problems if
+Linux is the only system on the disk. However, if the disk has
+to be shared with other operating systems, it is often a good idea
+to let an fdisk from another operating system make at least one
+partition. When Linux boots it looks at the partition table, and
+tries to deduce what (fake) geometry is required for good
+cooperation with other systems.
+
+Whenever a partition table is printed out, a consistency check is performed
+on the partition table entries. This check verifies that the physical and
+logical start and end points are identical, and that each partition starts
+and ends on a cylinder boundary (except for the first partition).
+
+Some versions of MS-DOS create a first partition which does not begin
+on a cylinder boundary, but on sector 2 of the first cylinder.
+Partitions beginning in cylinder 1 cannot begin on a cylinder boundary, but
+this is unlikely to cause difficulty unless you have OS/2 on your machine.
+
+A sync() and an ioctl(BLKRRPART) (reread partition table from disk)
+are performed before exiting when the partition table has been updated.
+Long ago it used to be necessary to reboot after the use of fdisk.
+I do not think this is the case anymore -- indeed, rebooting too quickly
+might cause loss of not-yet-written data. Note that both the kernel
+and the disk hardware may buffer data.
+
+.SH "DOS 6.x WARNING"
+
+The DOS 6.x FORMAT command looks for some information in the first
+sector of the data area of the partition, and treats this information
+as more reliable than the information in the partition table. DOS
+FORMAT expects DOS FDISK to clear the first 512 bytes of the data area
+of a partition whenever a size change occurs. DOS FORMAT will look at
+this extra information even if the /U flag is given -- we consider
+this a bug in DOS FORMAT and DOS FDISK.
+
+The bottom line is that if you use cfdisk or fdisk to change the size of a
+DOS partition table entry, then you must also use
+.B dd
+to zero the first 512 bytes of that partition before using DOS FORMAT to
+format the partition. For example, if you were using cfdisk to make a DOS
+partition table entry for /dev/sda1, then (after exiting fdisk or cfdisk
+and rebooting Linux so that the partition table information is valid) you
+would use the command "dd if=/dev/zero of=/dev/sda1 bs=512 count=1" to zero
+the first 512 bytes of the partition.
+
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+For best results, you should always use an OS-specific partition table
+program. For example, you should make DOS partitions with the DOS FDISK
+program and Linux partitions with the Linux fdisk or Linux cfdisk program.
+
+.SH OPTIONS
+.TP
+.BI "\-b " sectorsize
+Specify the sector size of the disk. Valid values are 512, 1024, 2048 or 4096.
+(Recent kernels know the sector size. Use this only on old kernels or
+to override the kernel's ideas.) Since util-linux-2.17, fdisk differentiates
+between logical and physical sector size. This option changes both sector sizes to
+.IB sectorsize .
+.TP
+.BI "\-c"[=mode]
+Specify the compatibility mode, 'dos' or 'nondos'. The default is non-DOS
+mode. For backward compatibility, it is possible to use the option without
+the <mode> argument -- then the default is used. Note that the optional
+<mode> argument cannot be separated from the -c option by a space, the correct
+form is for example '-c=dos'. This option is DEPRECATED.
+.TP
+.BI "\-C " cyls
+Specify the number of cylinders of the disk.
+I have no idea why anybody would want to do so. This option is DEPRECATED.
+.TP
+.BI "\-H " heads
+Specify the number of heads of the disk. (Not the physical number,
+of course, but the number used for partition tables.)
+Reasonable values are 255 and 16. This option is DEPRECATED.
+.TP
+.BI "\-S " sects
+Specify the number of sectors per track of the disk.
+(Not the physical number, of course, but the number used for
+partition tables.)
+A reasonable value is 63. This option is DEPRECATED.
+.TP
+.BI \-h
+Print help and then exit.
+.TP
+.B \-l
+List the partition tables for the specified devices and then exit.
+If no devices are given, those mentioned in
+.I /proc/partitions
+(if that exists) are used.
+.TP
+.BI "\-s " partition...
+Print the size (in blocks) of each given partition.
+.TP
+.BI "\-u"[=unit]
+When listing partition tables, show sizes in 'sectors' or in 'cylinders'. The
+default is to show sizes in sectors. For backward compatibility, it is possible
+to use the option without the <units> argument -- then the default is used.
+Note that the optional <unit> argument cannot be separated from the -u option
+by a space, the correct form is for example '-u=cylinders'.
+.TP
+.B \-v
+Print version number of
+.B fdisk
+program and exit.
+.SH BUGS
+There are several *fdisk programs around.
+Each has its problems and strengths.
+Try them in the order
+.BR cfdisk ,
+.BR fdisk ,
+.BR sfdisk .
+(Indeed,
+.B cfdisk
+is a beautiful program that has strict requirements on
+the partition tables it accepts, and produces high quality partition
+tables. Use it if you can.
+.B fdisk
+is a buggy program that does fuzzy things - usually it happens to
+produce reasonable results. Its single advantage is that it has
+some support for BSD disk labels and other non-DOS partition tables.
+Avoid it if you can.
+.B sfdisk
+is for hackers only -- the user interface is terrible, but it is
+more correct than fdisk and more powerful than both fdisk and cfdisk.
+Moreover, it can be used noninteractively.)
+.PP
+There also is
+.BR parted
+which supports many types of different partition table formats.
+.PP
+The IRIX/SGI-type disklabel is currently not supported by the kernel.
+Moreover, IRIX/SGI header directories are not fully supported yet.
+.PP
+The option `dump partition table to file' is missing.
+.\" .SH AUTHORS
+.\" A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+.\" Bernhard Fastenrath (fasten@informatik.uni-bonn.de)
+.\" Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+.\" Andreas Neuper (ANeuper@GUUG.de)
+.\" and many others.
+
+.SH ENVIRONMENT
+.IP FDISK_DEBUG=0xffff
+enables debug output
+
+.SH "SEE ALSO"
+.BR cfdisk (8),
+.BR sfdisk (8),
+.BR mkfs (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR kpartx (8)
+.SH AVAILABILITY
+The fdisk command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c
new file mode 100644
index 0000000..3a16c5f
--- /dev/null
+++ b/fdisks/fdisk.c
@@ -0,0 +1,1935 @@
+/* fdisk.c -- Partition table manipulator for Linux.
+ *
+ * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation: either version 1 or
+ * (at your option) any later version.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+
+#include "xalloc.h"
+#include "nls.h"
+#include "rpmatch.h"
+#include "blkdev.h"
+#include "common.h"
+#include "mbsalign.h"
+#include "fdisk.h"
+#include "wholedisk.h"
+#include "pathnames.h"
+#include "canonicalize.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#include "fdisksunlabel.h"
+#include "fdisksgilabel.h"
+#include "fdiskaixlabel.h"
+#include "fdiskmaclabel.h"
+#include "fdiskdoslabel.h"
+#include "fdiskbsdlabel.h"
+
+#ifdef HAVE_LINUX_COMPILER_H
+#include <linux/compiler.h>
+#endif
+#ifdef HAVE_LINUX_BLKPG_H
+#include <linux/blkpg.h>
+#endif
+
+#include "gpt.h"
+
+int MBRbuffer_changed;
+
+#define hex_val(c) ({ \
+ char _c = (c); \
+ isdigit(_c) ? _c - '0' : \
+ tolower(_c) + 10 - 'a'; \
+ })
+
+
+#define sector(s) ((s) & 0x3f)
+#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
+
+/* menu list description */
+
+struct menulist_descr {
+ char command; /* command key */
+ char *description; /* command description */
+ enum fdisk_labeltype label[2]; /* disklabel types associated with main and expert menu */
+};
+
+static const struct menulist_descr menulist[] = {
+ {'a', N_("toggle a bootable flag"), {DOS_LABEL, 0}},
+ {'a', N_("toggle a read only flag"), {SUN_LABEL, 0}},
+ {'a', N_("select bootable partition"), {SGI_LABEL, 0}},
+ {'a', N_("change number of alternate cylinders"), {0, SUN_LABEL}},
+ {'b', N_("edit bsd disklabel"), {DOS_LABEL, 0}},
+ {'b', N_("edit bootfile entry"), {SGI_LABEL, 0}},
+ {'b', N_("move beginning of data in a partition"), {0, DOS_LABEL}},
+ {'c', N_("toggle the dos compatibility flag"), {DOS_LABEL, 0}},
+ {'c', N_("toggle the mountable flag"), {SUN_LABEL, 0}},
+ {'c', N_("select sgi swap partition"), {SGI_LABEL, 0}},
+ {'c', N_("change number of cylinders"), {0, DOS_LABEL | SUN_LABEL}},
+ {'d', N_("delete a partition"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}},
+ {'d', N_("print the raw data in the partition table"), {0, ANY_LABEL}},
+ {'e', N_("change number of extra sectors per cylinder"), {0, SUN_LABEL}},
+ {'e', N_("list extended partitions"), {0, DOS_LABEL}},
+ {'e', N_("edit drive data"), {OSF_LABEL, 0}},
+ {'f', N_("fix partition order"), {0, DOS_LABEL}},
+ {'g', N_("create an IRIX (SGI) partition table"), {0, ANY_LABEL}},
+ {'h', N_("change number of heads"), {0, DOS_LABEL | SUN_LABEL}},
+ {'i', N_("change interleave factor"), {0, SUN_LABEL}},
+ {'i', N_("change the disk identifier"), {0, DOS_LABEL}},
+ {'i', N_("install bootstrap"), {OSF_LABEL, 0}},
+ {'l', N_("list known partition types"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}},
+ {'m', N_("print this menu"), {ANY_LABEL, ANY_LABEL}},
+ {'n', N_("add a new partition"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}},
+ {'o', N_("create a new empty DOS partition table"), {~OSF_LABEL, 0}},
+ {'o', N_("change rotation speed (rpm)"), {0, SUN_LABEL}},
+ {'p', N_("print the partition table"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, DOS_LABEL | SUN_LABEL}},
+ {'q', N_("quit without saving changes"), {ANY_LABEL, ANY_LABEL}},
+ {'r', N_("return to main menu"), {OSF_LABEL, DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL}},
+ {'s', N_("create a new empty Sun disklabel"), {~OSF_LABEL, 0}},
+ {'s', N_("change number of sectors/track"), {0, DOS_LABEL | SUN_LABEL}},
+ {'s', N_("show complete disklabel"), {OSF_LABEL, 0}},
+ {'t', N_("change a partition's system id"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}},
+ {'u', N_("change display/entry units"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}},
+ {'v', N_("verify the partition table"), {DOS_LABEL | SUN_LABEL | SGI_LABEL, DOS_LABEL | SUN_LABEL | SGI_LABEL}},
+ {'w', N_("write table to disk and exit"), {DOS_LABEL | SUN_LABEL | SGI_LABEL, DOS_LABEL | SUN_LABEL | SGI_LABEL}},
+ {'w', N_("write disklabel to disk"), {OSF_LABEL, 0}},
+ {'x', N_("extra functionality (experts only)"), {DOS_LABEL | SUN_LABEL | SGI_LABEL, 0}},
+#if !defined (__alpha__)
+ {'x', N_("link BSD partition to non-BSD partition"), {OSF_LABEL, 0}},
+#endif
+ {'y', N_("change number of physical cylinders"), {0, SUN_LABEL}},
+};
+
+sector_t get_nr_sects(struct partition *p) {
+ return read4_little_endian(p->size4);
+}
+
+char *line_ptr, /* interactive input */
+ line_buffer[LINE_LENGTH];
+
+int nowarn = 0, /* no warnings for fdisk -l/-s */
+ dos_compatible_flag = 0, /* disabled by default */
+ dos_changed = 0,
+ partitions = 4; /* maximum partition + 1 */
+
+unsigned int user_cylinders, user_heads, user_sectors;
+sector_t sector_offset = 1;
+unsigned int units_per_sector = 1, display_in_cyl_units = 0;
+enum fdisk_labeltype disklabel; /* Current disklabel */
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fprintf(out, _("Usage:\n"
+ " %1$s [options] <disk> change partition table\n"
+ " %1$s [options] -l <disk> list partition table(s)\n"
+ " %1$s -s <partition> give partition size(s) in blocks\n"
+ "\nOptions:\n"
+ " -b <size> sector size (512, 1024, 2048 or 4096)\n"
+ " -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n"
+ " -h print this help text\n"
+ " -u[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"
+ " -v print program version\n"
+ " -C <number> specify the number of cylinders\n"
+ " -H <number> specify the number of heads\n"
+ " -S <number> specify the number of sectors per track\n"
+ "\n"), program_invocation_short_name);
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+void __attribute__((__noreturn__))
+fatal(struct fdisk_context *cxt, enum failure why)
+{
+ close(cxt->dev_fd);
+ switch (why) {
+ case unable_to_read:
+ err(EXIT_FAILURE, _("unable to read %s"), cxt->dev_path);
+
+ case unable_to_seek:
+ err(EXIT_FAILURE, _("unable to seek on %s"), cxt->dev_path);
+
+ case unable_to_write:
+ err(EXIT_FAILURE, _("unable to write %s"), cxt->dev_path);
+
+ case ioctl_error:
+ err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), cxt->dev_path);
+
+ default:
+ err(EXIT_FAILURE, _("fatal error"));
+ }
+}
+
+struct partition *
+get_part_table(int i) {
+ return ptes[i].part_table;
+}
+
+void
+set_all_unchanged(void) {
+ int i;
+
+ for (i = 0; i < MAXIMUM_PARTS; i++)
+ ptes[i].changed = 0;
+}
+
+void
+set_changed(int i) {
+ ptes[i].changed = 1;
+}
+
+static int
+is_garbage_table(void) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (p->boot_ind != 0 && p->boot_ind != 0x80)
+ return 1;
+ }
+ return 0;
+}
+
+void print_menu(enum menutype menu)
+{
+ size_t i;
+
+ puts(_("Command action"));
+
+ for (i = 0; i < ARRAY_SIZE(menulist); i++)
+ if (menulist[i].label[menu] & disklabel)
+ printf(" %c %s\n", menulist[i].command, menulist[i].description);
+}
+
+static int
+get_sysid(struct fdisk_context *cxt, int i) {
+ return (
+ disklabel == SUN_LABEL ? sun_get_sysid(cxt, i) :
+ disklabel == SGI_LABEL ? sgi_get_sysid(cxt, i) :
+ ptes[i].part_table->sys_ind);
+}
+
+static struct systypes *
+get_sys_types(void) {
+ return (
+ disklabel == SUN_LABEL ? sun_sys_types :
+ disklabel == SGI_LABEL ? sgi_sys_types :
+ i386_sys_types);
+}
+
+char *partition_type(unsigned char type)
+{
+ int i;
+ struct systypes *types = get_sys_types();
+
+ for (i=0; types[i].name; i++)
+ if (types[i].type == type)
+ return _(types[i].name);
+
+ return NULL;
+}
+
+void list_types(struct systypes *sys)
+{
+ unsigned int last[4], done = 0, next = 0, size;
+ int i;
+
+ for (i = 0; sys[i].name; i++);
+ size = i;
+
+ for (i = 3; i >= 0; i--)
+ last[3 - i] = done += (size + i - done) / (i + 1);
+ i = done = 0;
+
+ do {
+ #define NAME_WIDTH 15
+ char name[NAME_WIDTH * MB_LEN_MAX];
+ size_t width = NAME_WIDTH;
+
+ printf("%c%2x ", i ? ' ' : '\n', sys[next].type);
+ size_t ret = mbsalign(_(sys[next].name), name, sizeof(name),
+ &width, MBS_ALIGN_LEFT, 0);
+ if (ret == (size_t)-1 || ret >= sizeof(name))
+ printf("%-15.15s", _(sys[next].name));
+ else
+ fputs(name, stdout);
+
+ next = last[i++] + done;
+ if (i > 3 || next >= last[i]) {
+ i = 0;
+ next = ++done;
+ }
+ } while (done < last[0]);
+ putchar('\n');
+}
+
+static int
+test_c(char **m, char *mesg) {
+ int val = 0;
+ if (!*m)
+ fprintf(stderr, _("You must set"));
+ else {
+ fprintf(stderr, " %s", *m);
+ val = 1;
+ }
+ *m = mesg;
+ return val;
+}
+
+static int
+lba_is_aligned(struct fdisk_context *cxt, sector_t lba)
+{
+ unsigned int granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+ unsigned long long offset;
+
+ if (cxt->grain > granularity)
+ granularity = cxt->grain;
+ offset = (lba * cxt->sector_size) & (granularity - 1);
+
+ return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+static int
+lba_is_phy_aligned(struct fdisk_context *cxt, unsigned long long lba)
+{
+ unsigned int granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+ unsigned long long offset = (lba * cxt->sector_size) & (granularity - 1);
+
+ return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+sector_t align_lba(struct fdisk_context *cxt, sector_t lba, int direction)
+{
+ sector_t res;
+
+ if (lba_is_aligned(cxt, lba))
+ res = lba;
+ else {
+ sector_t sects_in_phy = cxt->grain / cxt->sector_size;
+
+ if (lba < sector_offset)
+ res = sector_offset;
+
+ else if (direction == ALIGN_UP)
+ res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy;
+
+ else if (direction == ALIGN_DOWN)
+ res = (lba / sects_in_phy) * sects_in_phy;
+
+ else /* ALIGN_NEAREST */
+ res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy;
+
+ if (cxt->alignment_offset && !lba_is_aligned(cxt, res) &&
+ res > cxt->alignment_offset / cxt->sector_size) {
+ /*
+ * apply alignment_offset
+ *
+ * On disk with alignment compensation physical blocks starts
+ * at LBA < 0 (usually LBA -1). It means we have to move LBA
+ * according the offset to be on the physical boundary.
+ */
+ /* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */
+ res -= (max(cxt->phy_sector_size, cxt->min_io_size) -
+ cxt->alignment_offset) / cxt->sector_size;
+
+ if (direction == ALIGN_UP && res < lba)
+ res += sects_in_phy;
+ }
+ }
+
+ /***
+ fprintf(stderr, "LBA %llu (%s) --align-(%s)--> %llu (%s)\n",
+ lba,
+ lba_is_aligned(lba) ? "OK" : "FALSE",
+ direction == ALIGN_UP ? "UP " :
+ direction == ALIGN_DOWN ? "DOWN " : "NEAREST",
+ res,
+ lba_is_aligned(res) ? "OK" : "FALSE");
+ ***/
+ return res;
+}
+
+int warn_geometry(struct fdisk_context *cxt)
+{
+ char *m = NULL;
+ int prev = 0;
+
+ if (disklabel == SGI_LABEL) /* cannot set cylinders etc anyway */
+ return 0;
+ if (!cxt->geom.heads)
+ prev = test_c(&m, _("heads"));
+ if (!cxt->geom.sectors)
+ prev = test_c(&m, _("sectors"));
+ if (!cxt->geom.cylinders)
+ prev = test_c(&m, _("cylinders"));
+ if (!m)
+ return 0;
+ fprintf(stderr,
+ _("%s%s.\nYou can do this from the extra functions menu.\n"),
+ prev ? _(" and ") : " ", m);
+ return 1;
+}
+
+void update_units(struct fdisk_context *cxt)
+{
+ int cyl_units = cxt->geom.heads * cxt->geom.sectors;
+
+ if (display_in_cyl_units && cyl_units)
+ units_per_sector = cyl_units;
+ else
+ units_per_sector = 1; /* in sectors */
+}
+
+void warn_limits(struct fdisk_context *cxt)
+{
+ if (cxt->total_sectors > UINT_MAX && !nowarn) {
+ unsigned long long bytes = cxt->total_sectors * cxt->sector_size;
+ int giga = bytes / 1000000000;
+ int hectogiga = (giga + 50) / 100;
+
+ fprintf(stderr, _("\n"
+"WARNING: The size of this disk is %d.%d TB (%llu bytes).\n"
+"DOS partition table format can not be used on drives for volumes\n"
+"larger than (%llu bytes) for %ld-byte sectors. Use parted(1) and GUID \n"
+"partition table format (GPT).\n\n"),
+ hectogiga / 10, hectogiga % 10,
+ bytes,
+ (sector_t ) UINT_MAX * cxt->sector_size,
+ cxt->sector_size);
+ }
+}
+
+void warn_alignment(struct fdisk_context *cxt)
+{
+ if (nowarn)
+ return;
+
+ if (cxt->sector_size != cxt->phy_sector_size)
+ fprintf(stderr, _("\n"
+"The device presents a logical sector size that is smaller than\n"
+"the physical sector size. Aligning to a physical sector (or optimal\n"
+"I/O) size boundary is recommended, or performance may be impacted.\n"));
+
+ if (dos_compatible_flag)
+ fprintf(stderr, _("\n"
+"WARNING: DOS-compatible mode is deprecated. It's strongly recommended to\n"
+" switch off the mode (with command 'c')."));
+
+ if (display_in_cyl_units)
+ fprintf(stderr, _("\n"
+"WARNING: cylinders as display units are deprecated. Use command 'u' to\n"
+" change units to sectors.\n"));
+
+}
+
+/*
+ * Sets LBA of the first partition
+ */
+void
+update_sector_offset(struct fdisk_context *cxt)
+{
+ cxt->grain = cxt->io_size;
+
+ if (dos_compatible_flag)
+ sector_offset = cxt->geom.sectors; /* usually 63 sectors */
+ else {
+ /*
+ * Align the begin of partitions to:
+ *
+ * a) topology
+ * a2) alignment offset
+ * a1) or physical sector (minimal_io_size, aka "grain")
+ *
+ * b) or default to 1MiB (2048 sectrors, Windows Vista default)
+ *
+ * c) or for very small devices use 1 phy.sector
+ */
+ sector_t x = 0;
+
+ if (fdisk_dev_has_topology(cxt)) {
+ if (cxt->alignment_offset)
+ x = cxt->alignment_offset;
+ else if (cxt->io_size > 2048 * 512)
+ x = cxt->io_size;
+ }
+ /* default to 1MiB */
+ if (!x)
+ x = 2048 * 512;
+
+ sector_offset = x / cxt->sector_size;
+
+ /* don't use huge offset on small devices */
+ if (cxt->total_sectors <= sector_offset * 4)
+ sector_offset = cxt->phy_sector_size / cxt->sector_size;
+
+ /* use 1MiB grain always when possible */
+ if (cxt->grain < 2048 * 512)
+ cxt->grain = 2048 * 512;
+
+ /* don't use huge grain on small devices */
+ if (cxt->total_sectors <= (cxt->grain * 4 / cxt->sector_size))
+ cxt->grain = cxt->phy_sector_size;
+ }
+}
+
+static int is_partition_table_changed(void)
+{
+ int i;
+
+ for (i = 0; i < partitions; i++)
+ if (ptes[i].changed)
+ return 1;
+ return 0;
+}
+
+static void maybe_exit(int rc, int *asked)
+{
+ char line[LINE_LENGTH];
+
+ putchar('\n');
+ if (asked)
+ *asked = 0;
+
+ if (is_partition_table_changed() || MBRbuffer_changed) {
+ fprintf(stderr, _("Do you really want to quit? "));
+
+ if (!fgets(line, LINE_LENGTH, stdin) || rpmatch(line) == 1)
+ exit(rc);
+ if (asked)
+ *asked = 1;
+ } else
+ exit(rc);
+}
+
+/* read line; return 0 or first char */
+int
+read_line(int *asked)
+{
+ line_ptr = line_buffer;
+ if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
+ maybe_exit(1, asked);
+ return 0;
+ }
+ if (asked)
+ *asked = 0;
+ while (*line_ptr && !isgraph(*line_ptr))
+ line_ptr++;
+ return *line_ptr;
+}
+
+char
+read_char(char *mesg)
+{
+ do {
+ fputs(mesg, stdout);
+ fflush (stdout); /* requested by niles@scyld.com */
+ } while (!read_line(NULL));
+ return *line_ptr;
+}
+
+char
+read_chars(char *mesg)
+{
+ int rc, asked = 0;
+
+ do {
+ fputs(mesg, stdout);
+ fflush (stdout); /* niles@scyld.com */
+ rc = read_line(&asked);
+ } while (asked);
+
+ if (!rc) {
+ *line_ptr = '\n';
+ line_ptr[1] = 0;
+ }
+ return *line_ptr;
+}
+
+int
+read_hex(struct systypes *sys)
+{
+ int hex;
+
+ while (1)
+ {
+ read_char(_("Hex code (type L to list codes): "));
+ if (tolower(*line_ptr) == 'l')
+ list_types(sys);
+ else if (isxdigit (*line_ptr))
+ {
+ hex = 0;
+ do
+ hex = hex << 4 | hex_val(*line_ptr++);
+ while (isxdigit(*line_ptr));
+ return hex;
+ }
+ }
+}
+
+unsigned int
+read_int_with_suffix(struct fdisk_context *cxt,
+ unsigned int low, unsigned int dflt, unsigned int high,
+ unsigned int base, char *mesg, int *is_suffix_used)
+{
+ unsigned int res;
+ int default_ok = 1;
+ int absolute = 0;
+ static char *ms = NULL;
+ static size_t mslen = 0;
+
+ if (!ms || strlen(mesg)+100 > mslen) {
+ mslen = strlen(mesg)+200;
+ ms = xrealloc(ms,mslen);
+ }
+
+ if (dflt < low || dflt > high)
+ default_ok = 0;
+
+ if (default_ok)
+ snprintf(ms, mslen, _("%s (%u-%u, default %u): "),
+ mesg, low, high, dflt);
+ else
+ snprintf(ms, mslen, "%s (%u-%u): ",
+ mesg, low, high);
+
+ while (1) {
+ int use_default = default_ok;
+
+ /* ask question and read answer */
+ while (read_chars(ms) != '\n' && !isdigit(*line_ptr)
+ && *line_ptr != '-' && *line_ptr != '+')
+ continue;
+
+ if (*line_ptr == '+' || *line_ptr == '-') {
+ int minus = (*line_ptr == '-');
+ int suflen;
+
+ absolute = 0;
+ res = atoi(line_ptr + 1);
+
+ while (isdigit(*++line_ptr))
+ use_default = 0;
+
+ while (isspace(*line_ptr))
+ line_ptr++;
+
+ suflen = strlen(line_ptr) - 1;
+
+ while(isspace(*(line_ptr + suflen)))
+ *(line_ptr + suflen--) = '\0';
+
+ if ((*line_ptr == 'C' || *line_ptr == 'c') &&
+ *(line_ptr + 1) == '\0') {
+ /*
+ * Cylinders
+ */
+ if (!display_in_cyl_units)
+ res *= cxt->geom.heads * cxt->geom.sectors;
+ } else if (*line_ptr &&
+ *(line_ptr + 1) == 'B' &&
+ *(line_ptr + 2) == '\0') {
+ /*
+ * 10^N
+ */
+ if (*line_ptr == 'K')
+ absolute = 1000;
+ else if (*line_ptr == 'M')
+ absolute = 1000000;
+ else if (*line_ptr == 'G')
+ absolute = 1000000000;
+ else
+ absolute = -1;
+ } else if (*line_ptr &&
+ *(line_ptr + 1) == '\0') {
+ /*
+ * 2^N
+ */
+ if (*line_ptr == 'K')
+ absolute = 1 << 10;
+ else if (*line_ptr == 'M')
+ absolute = 1 << 20;
+ else if (*line_ptr == 'G')
+ absolute = 1 << 30;
+ else
+ absolute = -1;
+ } else if (*line_ptr != '\0')
+ absolute = -1;
+
+ if (absolute == -1) {
+ printf(_("Unsupported suffix: '%s'.\n"), line_ptr);
+ printf(_("Supported: 10^N: KB (KiloByte), MB (MegaByte), GB (GigaByte)\n"
+ " 2^N: K (KibiByte), M (MebiByte), G (GibiByte)\n"));
+ continue;
+ }
+
+ if (absolute && res) {
+ unsigned long long bytes;
+ unsigned long unit;
+
+ bytes = (unsigned long long) res * absolute;
+ unit = cxt->sector_size * units_per_sector;
+ bytes += unit/2; /* round */
+ bytes /= unit;
+ res = bytes;
+ }
+ if (minus)
+ res = -res;
+ res += base;
+ } else {
+ res = atoi(line_ptr);
+ while (isdigit(*line_ptr)) {
+ line_ptr++;
+ use_default = 0;
+ }
+ }
+ if (use_default) {
+ printf(_("Using default value %u\n"), dflt);
+ return dflt;
+ }
+ if (res >= low && res <= high)
+ break;
+ else
+ printf(_("Value out of range.\n"));
+ }
+ if (is_suffix_used)
+ *is_suffix_used = absolute > 0;
+ return res;
+}
+
+/*
+ * Print the message MESG, then read an integer in LOW..HIGH.
+ * If the user hits Enter, DFLT is returned, provided that is in LOW..HIGH.
+ * Answers like +10 are interpreted as offsets from BASE.
+ *
+ * There is no default if DFLT is not between LOW and HIGH.
+ */
+unsigned int
+read_int(struct fdisk_context *cxt,
+ unsigned int low, unsigned int dflt, unsigned int high,
+ unsigned int base, char *mesg)
+{
+ return read_int_with_suffix(cxt, low, dflt, high, base, mesg, NULL);
+}
+
+int
+get_partition_dflt(struct fdisk_context *cxt, int warn, int max, int dflt) {
+ struct pte *pe;
+ int i;
+
+ i = read_int(cxt, 1, dflt, max, 0, _("Partition number")) - 1;
+ pe = &ptes[i];
+
+ if (warn) {
+ if ((disklabel != SUN_LABEL && disklabel != SGI_LABEL && !pe->part_table->sys_ind)
+ || (disklabel == SUN_LABEL &&
+ (!sunlabel->partitions[i].num_sectors ||
+ !sunlabel->part_tags[i].tag))
+ || (disklabel == SGI_LABEL && (!sgi_get_num_sectors(cxt, i)))
+ )
+ fprintf(stderr,
+ _("Warning: partition %d has empty type\n"),
+ i+1);
+ }
+ return i;
+}
+
+int
+get_partition(struct fdisk_context *cxt, int warn, int max) {
+ return get_partition_dflt(cxt, warn, max, 0);
+}
+
+/* User partition selection unless one partition only is available */
+
+static int
+get_existing_partition(struct fdisk_context *cxt, int warn, int max) {
+ int pno = -1;
+ int i;
+
+ if (disklabel != DOS_LABEL)
+ goto not_implemented;
+
+ for (i = 0; i < max; i++) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (p && !is_cleared_partition(p)) {
+ if (pno >= 0)
+ goto not_unique;
+ pno = i;
+ }
+ }
+
+ if (pno >= 0) {
+ printf(_("Selected partition %d\n"), pno+1);
+ return pno;
+ }
+ printf(_("No partition is defined yet!\n"));
+ return -1;
+
+not_implemented:
+not_unique:
+ return get_partition(cxt, warn, max);
+}
+
+const char *
+str_units(int n)
+{
+ if (display_in_cyl_units)
+ return P_("cylinder", "cylinders", n);
+ return P_("sector", "sectors", n);
+}
+
+void change_units(struct fdisk_context *cxt)
+{
+ display_in_cyl_units = !display_in_cyl_units;
+ update_units(cxt);
+
+ if (display_in_cyl_units)
+ printf(_("Changing display/entry units to cylinders (DEPRECATED!)\n"));
+ else
+ printf(_("Changing display/entry units to sectors\n"));
+}
+
+static void
+toggle_active(int i) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (IS_EXTENDED (p->sys_ind) && !p->boot_ind)
+ fprintf(stderr,
+ _("WARNING: Partition %d is an extended partition\n"),
+ i + 1);
+ p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+ pe->changed = 1;
+}
+
+static void
+toggle_dos_compatibility_flag(struct fdisk_context *cxt) {
+ dos_compatible_flag = ~dos_compatible_flag;
+ if (dos_compatible_flag)
+ printf(_("DOS Compatibility flag is set (DEPRECATED!)\n"));
+ else
+ printf(_("DOS Compatibility flag is not set\n"));
+
+ update_sector_offset(cxt);
+}
+
+static void delete_partition(struct fdisk_context *cxt, int partnum)
+{
+ if (partnum < 0 || warn_geometry(cxt))
+ return;
+
+ ptes[partnum].changed = 1;
+ fdisk_delete_partition(cxt, partnum);
+ printf(_("Partition %d is deleted\n"), partnum + 1);
+}
+
+static void change_sysid(struct fdisk_context *cxt)
+{
+ char *temp;
+ int i, sys, origsys;
+ struct partition *p;
+
+ i = get_existing_partition(cxt, 0, partitions);
+
+ if (i == -1)
+ return;
+ p = ptes[i].part_table;
+ origsys = sys = get_sysid(cxt, i);
+
+ /* if changing types T to 0 is allowed, then
+ the reverse change must be allowed, too */
+ if (!sys && disklabel != SGI_LABEL && disklabel != SUN_LABEL && !get_nr_sects(p))
+ printf(_("Partition %d does not exist yet!\n"), i + 1);
+ else while (1) {
+ sys = read_hex (get_sys_types());
+
+ if (!sys && disklabel != SGI_LABEL && disklabel != SUN_LABEL) {
+ printf(_("Type 0 means free space to many systems\n"
+ "(but not to Linux). Having partitions of\n"
+ "type 0 is probably unwise. You can delete\n"
+ "a partition using the `d' command.\n"));
+ /* break; */
+ }
+
+ if (disklabel != SGI_LABEL && disklabel != SUN_LABEL) {
+ if (IS_EXTENDED (sys) != IS_EXTENDED (p->sys_ind)) {
+ printf(_("You cannot change a partition into"
+ " an extended one or vice versa\n"
+ "Delete it first.\n"));
+ break;
+ }
+ }
+
+ if (sys < 256) {
+ if (disklabel == SUN_LABEL && i == 2 && sys != SUN_TAG_BACKUP)
+ printf(_("Consider leaving partition 3 "
+ "as Whole disk (5),\n"
+ "as SunOS/Solaris expects it and "
+ "even Linux likes it.\n\n"));
+ if (disklabel == SGI_LABEL && ((i == 10 && sys != ENTIRE_DISK)
+ || (i == 8 && sys != 0)))
+ printf(_("Consider leaving partition 9 "
+ "as volume header (0),\nand "
+ "partition 11 as entire volume (6), "
+ "as IRIX expects it.\n\n"));
+ if (sys == origsys)
+ break;
+ if (disklabel == SUN_LABEL) {
+ ptes[i].changed = sun_change_sysid(cxt, i, sys);
+ } else
+ if (disklabel == SGI_LABEL) {
+ ptes[i].changed = sgi_change_sysid(cxt, i, sys);
+ } else {
+ p->sys_ind = sys;
+ ptes[i].changed = 1;
+ }
+ temp = partition_type(sys) ? : _("Unknown");
+ if (ptes[i].changed)
+ printf (_("Changed system type of partition %d "
+ "to %x (%s)\n"), i + 1, sys, temp);
+ else
+ printf (_("System type of partition %d is unchanged: "
+ "%x (%s)\n"), i + 1, sys, temp);
+ if (is_dos_partition(origsys) ||
+ is_dos_partition(sys))
+ dos_changed = 1;
+ break;
+ }
+ }
+}
+
+/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct. 1991). */
+
+static void
+long2chs(struct fdisk_context *cxt, unsigned long ls,
+ unsigned int *c, unsigned int *h, unsigned int *s) {
+ int spc = cxt->geom.heads * cxt->geom.sectors;
+
+ *c = ls / spc;
+ ls = ls % spc;
+ *h = ls / cxt->geom.sectors;
+ *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
+}
+
+void check_consistency(struct fdisk_context *cxt, struct partition *p, int partition)
+{
+ unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
+ unsigned int pec, peh, pes; /* physical ending c, h, s */
+ unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
+ unsigned int lec, leh, les; /* logical ending c, h, s */
+
+ if (!dos_compatible_flag)
+ return;
+
+ if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
+ return; /* do not check extended partitions */
+
+/* physical beginning c, h, s */
+ pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
+ pbh = p->head;
+ pbs = p->sector & 0x3f;
+
+/* physical ending c, h, s */
+ pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
+ peh = p->end_head;
+ pes = p->end_sector & 0x3f;
+
+/* compute logical beginning (c, h, s) */
+ long2chs(cxt, get_start_sect(p), &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+ long2chs(cxt, get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
+
+/* Same physical / logical beginning? */
+ if (cxt->geom.cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+ printf(_("Partition %d has different physical/logical "
+ "beginnings (non-Linux?):\n"), partition + 1);
+ printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs);
+ printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs);
+ }
+
+/* Same physical / logical ending? */
+ if (cxt->geom.cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+ printf(_("Partition %d has different physical/logical "
+ "endings:\n"), partition + 1);
+ printf(_(" phys=(%d, %d, %d) "), pec, peh, pes);
+ printf(_("logical=(%d, %d, %d)\n"),lec, leh, les);
+ }
+
+/* Ending on cylinder boundary? */
+ if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
+ printf(_("Partition %i does not end on cylinder boundary.\n"),
+ partition + 1);
+ }
+}
+
+void check_alignment(struct fdisk_context *cxt, sector_t lba, int partition)
+{
+ if (!lba_is_phy_aligned(cxt, lba))
+ printf(_("Partition %i does not start on physical sector boundary.\n"),
+ partition + 1);
+}
+
+static void
+list_disk_geometry(struct fdisk_context *cxt) {
+ unsigned long long bytes = cxt->total_sectors * cxt->sector_size;
+ long megabytes = bytes/1000000;
+
+ if (megabytes < 10000)
+ printf(_("\nDisk %s: %ld MB, %lld bytes"),
+ cxt->dev_path, megabytes, bytes);
+ else {
+ long hectomega = (megabytes + 50) / 100;
+ printf(_("\nDisk %s: %ld.%ld GB, %llu bytes"),
+ cxt->dev_path, hectomega / 10, hectomega % 10, bytes);
+ }
+ printf(_(", %llu sectors\n"), cxt->total_sectors);
+ if (dos_compatible_flag)
+ printf(_("%d heads, %llu sectors/track, %llu cylinders\n"),
+ cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders);
+ printf(_("Units = %s of %d * %ld = %ld bytes\n"),
+ str_units(PLURAL),
+ units_per_sector, cxt->sector_size, units_per_sector * cxt->sector_size);
+
+ printf(_("Sector size (logical/physical): %lu bytes / %lu bytes\n"),
+ cxt->sector_size, cxt->phy_sector_size);
+ printf(_("I/O size (minimum/optimal): %lu bytes / %lu bytes\n"),
+ cxt->min_io_size, cxt->io_size);
+ if (cxt->alignment_offset)
+ printf(_("Alignment offset: %lu bytes\n"), cxt->alignment_offset);
+ if (disklabel == DOS_LABEL)
+ dos_print_mbr_id(cxt);
+ printf("\n");
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int
+wrong_p_order(int *prev) {
+ struct pte *pe;
+ struct partition *p;
+ unsigned int last_p_start_pos = 0, p_start_pos;
+ int i, last_i = 0;
+
+ for (i = 0 ; i < partitions; i++) {
+ if (i == 4) {
+ last_i = 4;
+ last_p_start_pos = 0;
+ }
+ pe = &ptes[i];
+ if ((p = pe->part_table)->sys_ind) {
+ p_start_pos = get_partition_start(pe);
+
+ if (last_p_start_pos > p_start_pos) {
+ if (prev)
+ *prev = last_i;
+ return i;
+ }
+
+ last_p_start_pos = p_start_pos;
+ last_i = i;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Fix the chain of logicals.
+ * extended_offset is unchanged, the set of sectors used is unchanged
+ * The chain is sorted so that sectors increase, and so that
+ * starting sectors increase.
+ *
+ * After this it may still be that cfdisk doesn't like the table.
+ * (This is because cfdisk considers expanded parts, from link to
+ * end of partition, and these may still overlap.)
+ * Now
+ * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
+ * may help.
+ */
+static void
+fix_chain_of_logicals(void) {
+ int j, oj, ojj, sj, sjj;
+ struct partition *pj,*pjj,tmp;
+
+ /* Stage 1: sort sectors but leave sector of part 4 */
+ /* (Its sector is the global extended_offset.) */
+ stage1:
+ for (j = 5; j < partitions-1; j++) {
+ oj = ptes[j].offset;
+ ojj = ptes[j+1].offset;
+ if (oj > ojj) {
+ ptes[j].offset = ojj;
+ ptes[j+1].offset = oj;
+ pj = ptes[j].part_table;
+ set_start_sect(pj, get_start_sect(pj)+oj-ojj);
+ pjj = ptes[j+1].part_table;
+ set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
+ set_start_sect(ptes[j-1].ext_pointer,
+ ojj-extended_offset);
+ set_start_sect(ptes[j].ext_pointer,
+ oj-extended_offset);
+ goto stage1;
+ }
+ }
+
+ /* Stage 2: sort starting sectors */
+ stage2:
+ for (j = 4; j < partitions-1; j++) {
+ pj = ptes[j].part_table;
+ pjj = ptes[j+1].part_table;
+ sj = get_start_sect(pj);
+ sjj = get_start_sect(pjj);
+ oj = ptes[j].offset;
+ ojj = ptes[j+1].offset;
+ if (oj+sj > ojj+sjj) {
+ tmp = *pj;
+ *pj = *pjj;
+ *pjj = tmp;
+ set_start_sect(pj, ojj+sjj-oj);
+ set_start_sect(pjj, oj+sj-ojj);
+ goto stage2;
+ }
+ }
+
+ /* Probably something was changed */
+ for (j = 4; j < partitions; j++)
+ ptes[j].changed = 1;
+}
+
+static void
+fix_partition_table_order(void) {
+ struct pte *pei, *pek;
+ int i,k;
+
+ if (!wrong_p_order(NULL)) {
+ printf(_("Nothing to do. Ordering is correct already.\n\n"));
+ return;
+ }
+
+ while ((i = wrong_p_order(&k)) != 0 && i < 4) {
+ /* partition i should have come earlier, move it */
+ /* We have to move data in the MBR */
+ struct partition *pi, *pk, *pe, pbuf;
+ pei = &ptes[i];
+ pek = &ptes[k];
+
+ pe = pei->ext_pointer;
+ pei->ext_pointer = pek->ext_pointer;
+ pek->ext_pointer = pe;
+
+ pi = pei->part_table;
+ pk = pek->part_table;
+
+ memmove(&pbuf, pi, sizeof(struct partition));
+ memmove(pi, pk, sizeof(struct partition));
+ memmove(pk, &pbuf, sizeof(struct partition));
+
+ pei->changed = pek->changed = 1;
+ }
+
+ if (i)
+ fix_chain_of_logicals();
+
+ printf(_("Done.\n"));
+
+}
+
+static void
+list_table(struct fdisk_context *cxt, int xtra) {
+ struct partition *p;
+ char *type;
+ int i, w;
+
+ if (disklabel == SUN_LABEL) {
+ sun_list_table(cxt, xtra);
+ return;
+ }
+
+ if (disklabel == SGI_LABEL) {
+ sgi_list_table(cxt, xtra);
+ return;
+ }
+
+ list_disk_geometry(cxt);
+
+ if (disklabel == OSF_LABEL) {
+ xbsd_print_disklabel(cxt, xtra);
+ return;
+ }
+
+ if (is_garbage_table()) {
+ printf(_("This doesn't look like a partition table\n"
+ "Probably you selected the wrong device.\n\n"));
+ }
+
+ /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
+ but if the device name ends in a digit, say /dev/foo1,
+ then the partition is called /dev/foo1p3. */
+ w = strlen(cxt->dev_path);
+ if (w && isdigit(cxt->dev_path[w-1]))
+ w++;
+ if (w < 5)
+ w = 5;
+
+ printf(_("%*s Boot Start End Blocks Id System\n"),
+ w+1, _("Device"));
+
+ for (i = 0; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ p = pe->part_table;
+ if (p && !is_cleared_partition(p)) {
+ unsigned int psects = get_nr_sects(p);
+ unsigned int pblocks = psects;
+ unsigned int podd = 0;
+
+ if (cxt->sector_size < 1024) {
+ pblocks /= (1024 / cxt->sector_size);
+ podd = psects % (1024 / cxt->sector_size);
+ }
+ if (cxt->sector_size > 1024)
+ pblocks *= (cxt->sector_size / 1024);
+ printf(
+ "%s %c %11lu %11lu %11lu%c %2x %s\n",
+ partname(cxt->dev_path, i+1, w+2),
+/* boot flag */ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
+ ? '*' : '?',
+/* start */ (unsigned long) cround(get_partition_start(pe)),
+/* end */ (unsigned long) cround(get_partition_start(pe) + psects
+ - (psects ? 1 : 0)),
+/* odd flag on end */ (unsigned long) pblocks, podd ? '+' : ' ',
+/* type id */ p->sys_ind,
+/* type name */ (type = partition_type(p->sys_ind)) ?
+ type : _("Unknown"));
+ check_consistency(cxt, p, i);
+ check_alignment(cxt, get_partition_start(pe), i);
+ }
+ }
+
+ /* Is partition table in disk order? It need not be, but... */
+ /* partition table entries are not checked for correct order if this
+ is a sgi, sun or aix labeled disk... */
+ if (disklabel == DOS_LABEL && wrong_p_order(NULL)) {
+ printf(_("\nPartition table entries are not in disk order\n"));
+ }
+}
+
+static void
+x_list_table(struct fdisk_context *cxt, int extend) {
+ struct pte *pe;
+ struct partition *p;
+ int i;
+
+ printf(_("\nDisk %s: %d heads, %llu sectors, %llu cylinders\n\n"),
+ cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders);
+ printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"));
+ for (i = 0 ; i < partitions; i++) {
+ pe = &ptes[i];
+ p = (extend ? pe->ext_pointer : pe->part_table);
+ if (p != NULL) {
+ printf("%2d %02x%4d%4d%5d%4d%4d%5d%11lu%11lu %02x\n",
+ i + 1, p->boot_ind, p->head,
+ sector(p->sector),
+ cylinder(p->sector, p->cyl), p->end_head,
+ sector(p->end_sector),
+ cylinder(p->end_sector, p->end_cyl),
+ (unsigned long) get_start_sect(p),
+ (unsigned long) get_nr_sects(p), p->sys_ind);
+ if (p->sys_ind) {
+ check_consistency(cxt, p, i);
+ check_alignment(cxt, get_partition_start(pe), i);
+ }
+ }
+ }
+}
+
+void fill_bounds(sector_t *first, sector_t *last)
+{
+ int i;
+ struct pte *pe = &ptes[0];
+ struct partition *p;
+
+ for (i = 0; i < partitions; pe++,i++) {
+ p = pe->part_table;
+ if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) {
+ first[i] = 0xffffffff;
+ last[i] = 0;
+ } else {
+ first[i] = get_partition_start(pe);
+ last[i] = first[i] + get_nr_sects(p) - 1;
+ }
+ }
+}
+
+void check(struct fdisk_context *cxt, int n,
+ unsigned int h, unsigned int s, unsigned int c,
+ unsigned int start)
+{
+ unsigned int total, real_s, real_c;
+
+ real_s = sector(s) - 1;
+ real_c = cylinder(s, c);
+ total = (real_c * cxt->geom.sectors + real_s) * cxt->geom.heads + h;
+ if (!total)
+ fprintf(stderr, _("Warning: partition %d contains sector 0\n"), n);
+ if (h >= cxt->geom.heads)
+ fprintf(stderr,
+ _("Partition %d: head %d greater than maximum %d\n"),
+ n, h + 1, cxt->geom.heads);
+ if (real_s >= cxt->geom.sectors)
+ fprintf(stderr, _("Partition %d: sector %d greater than "
+ "maximum %llu\n"), n, s, cxt->geom.sectors);
+ if (real_c >= cxt->geom.cylinders)
+ fprintf(stderr, _("Partitions %d: cylinder %d greater than "
+ "maximum %llu\n"), n, real_c + 1, cxt->geom.cylinders);
+ if (cxt->geom.cylinders <= 1024 && start != total)
+ fprintf(stderr,
+ _("Partition %d: previous sectors %d disagrees with "
+ "total %d\n"), n, start, total);
+}
+
+static void verify(struct fdisk_context *cxt)
+{
+ if (warn_geometry(cxt))
+ return;
+
+ fdisk_verify_disklabel(cxt);
+}
+
+void print_partition_size(struct fdisk_context *cxt,
+ int num, sector_t start, sector_t stop, int sysid)
+{
+ char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
+ (uint64_t)(stop - start + 1) * cxt->sector_size);
+ printf(_("Partition %d of type %s and of size %s is set\n"), num, partition_type(sysid), str);
+ free(str);
+}
+
+static void new_partition(struct fdisk_context *cxt)
+{
+ int partnum = 0;
+
+ if (warn_geometry(cxt))
+ return;
+
+ if (disklabel == SUN_LABEL || disklabel == SGI_LABEL)
+ partnum = get_partition(cxt, 0, partitions);
+
+ /*
+ * Use default LINUX_NATIVE partition type, DOS labels
+ * may override this internally.
+ */
+ fdisk_add_partition(cxt, partnum, LINUX_NATIVE);
+}
+
+static void write_table(struct fdisk_context *cxt)
+{
+ int rc;
+
+ rc = fdisk_write_disklabel(cxt);
+ if (rc)
+ err(EXIT_FAILURE, _("cannot write disk label"));
+
+ printf(_("The partition table has been altered!\n\n"));
+ reread_partition_table(cxt, 1);
+}
+
+void
+reread_partition_table(struct fdisk_context *cxt, int leave) {
+ int i;
+ struct stat statbuf;
+
+ i = fstat(cxt->dev_fd, &statbuf);
+ if (i == 0 && S_ISBLK(statbuf.st_mode)) {
+ sync();
+#ifdef BLKRRPART
+ printf(_("Calling ioctl() to re-read partition table.\n"));
+ i = ioctl(cxt->dev_fd, BLKRRPART);
+#else
+ errno = ENOSYS;
+ i = 1;
+#endif
+ }
+
+ if (i) {
+ printf(_("\nWARNING: Re-reading the partition table failed with error %d: %m.\n"
+ "The kernel still uses the old table. The new table will be used at\n"
+ "the next reboot or after you run partprobe(8) or kpartx(8)\n"),
+ errno);
+ }
+
+ if (dos_changed)
+ printf(
+ _("\nWARNING: If you have created or modified any DOS 6.x\n"
+ "partitions, please see the fdisk manual page for additional\n"
+ "information.\n"));
+
+ if (leave) {
+ if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
+ fprintf(stderr, _("\nError closing file\n"));
+ exit(1);
+ }
+
+ printf(_("Syncing disks.\n"));
+ sync();
+ exit(!!i);
+ }
+}
+
+#define MAX_PER_LINE 16
+static void
+print_buffer(struct fdisk_context *cxt, unsigned char pbuffer[]) {
+ unsigned int i, l;
+
+ for (i = 0, l = 0; i < cxt->sector_size; i++, l++) {
+ if (l == 0)
+ printf("0x%03X:", i);
+ printf(" %02X", pbuffer[i]);
+ if (l == MAX_PER_LINE - 1) {
+ printf("\n");
+ l = -1;
+ }
+ }
+ if (l > 0)
+ printf("\n");
+ printf("\n");
+}
+
+static void print_raw(struct fdisk_context *cxt)
+{
+ int i;
+
+ printf(_("Device: %s\n"), cxt->dev_path);
+ if (disklabel == SUN_LABEL || disklabel == SGI_LABEL)
+ print_buffer(cxt, cxt->firstsector);
+ else for (i = 3; i < partitions; i++)
+ print_buffer(cxt, ptes[i].sectorbuffer);
+}
+
+static void
+move_begin(struct fdisk_context *cxt, int i) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+ unsigned int new, free_start, curr_start, last;
+ int x;
+
+ if (warn_geometry(cxt))
+ return;
+ if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED (p->sys_ind)) {
+ printf(_("Partition %d has no data area\n"), i + 1);
+ return;
+ }
+
+ /* the default start is at the second sector of the disk or at the
+ * second sector of the extended partition
+ */
+ free_start = pe->offset ? pe->offset + 1 : 1;
+
+ curr_start = get_partition_start(pe);
+
+ /* look for a free space before the current start of the partition */
+ for (x = 0; x < partitions; x++) {
+ unsigned int end;
+ struct pte *prev_pe = &ptes[x];
+ struct partition *prev_p = prev_pe->part_table;
+
+ if (!prev_p)
+ continue;
+ end = get_partition_start(prev_pe) + get_nr_sects(prev_p);
+
+ if (!is_cleared_partition(prev_p) &&
+ end > free_start && end <= curr_start)
+ free_start = end;
+ }
+
+ last = get_partition_start(pe) + get_nr_sects(p) - 1;
+
+ new = read_int(cxt, free_start, curr_start, last, free_start,
+ _("New beginning of data")) - pe->offset;
+
+ if (new != get_nr_sects(p)) {
+ unsigned int sects = get_nr_sects(p) + get_start_sect(p) - new;
+ set_nr_sects(p, sects);
+ set_start_sect(p, new);
+ pe->changed = 1;
+ }
+}
+
+static void __attribute__ ((__noreturn__)) handle_quit(struct fdisk_context *cxt)
+{
+ fdisk_free_context(cxt);
+ printf("\n");
+ exit(EXIT_SUCCESS);
+}
+
+static void
+expert_command_prompt(struct fdisk_context *cxt)
+{
+ char c;
+
+ while(1) {
+ putchar('\n');
+ c = tolower(read_char(_("Expert command (m for help): ")));
+ switch (c) {
+ case 'a':
+ if (disklabel == SUN_LABEL)
+ sun_set_alt_cyl(cxt);
+ break;
+ case 'b':
+ if (disklabel == DOS_LABEL)
+ move_begin(cxt, get_partition(cxt, 0, partitions));
+ break;
+ case 'c':
+ user_cylinders = cxt->geom.cylinders =
+ read_int(cxt, 1, cxt->geom.cylinders, 1048576, 0,
+ _("Number of cylinders"));
+ if (disklabel == SUN_LABEL)
+ sun_set_ncyl(cxt, cxt->geom.cylinders);
+ break;
+ case 'd':
+ print_raw(cxt);
+ break;
+ case 'e':
+ if (disklabel == SGI_LABEL)
+ sgi_set_xcyl();
+ else if (disklabel == SUN_LABEL)
+ sun_set_xcyl(cxt);
+ else
+ if (disklabel == DOS_LABEL)
+ x_list_table(cxt, 1);
+ break;
+ case 'f':
+ if (disklabel == DOS_LABEL)
+ fix_partition_table_order();
+ break;
+ case 'g':
+ fdisk_create_disklabel(cxt, "sgi");
+ break;
+ case 'h':
+ user_heads = cxt->geom.heads = read_int(cxt, 1, cxt->geom.heads, 256, 0,
+ _("Number of heads"));
+ update_units(cxt);
+ break;
+ case 'i':
+ if (disklabel == SUN_LABEL)
+ sun_set_ilfact(cxt);
+ else if (disklabel == DOS_LABEL)
+ dos_set_mbr_id(cxt);
+ break;
+ case 'o':
+ if (disklabel == SUN_LABEL)
+ sun_set_rspeed(cxt);
+ break;
+ case 'p':
+ if (disklabel == SUN_LABEL)
+ list_table(cxt, 1);
+ else
+ x_list_table(cxt, 0);
+ break;
+ case 'q':
+ handle_quit(cxt);
+ case 'r':
+ return;
+ case 's':
+ user_sectors = cxt->geom.sectors = read_int(cxt, 1, cxt->geom.sectors, 63, 0,
+ _("Number of sectors"));
+ if (dos_compatible_flag)
+ fprintf(stderr, _("Warning: setting "
+ "sector offset for DOS "
+ "compatibility\n"));
+ update_sector_offset(cxt);
+ update_units(cxt);
+ break;
+ case 'v':
+ verify(cxt);
+ break;
+ case 'w':
+ write_table(cxt);
+ break;
+ case 'y':
+ if (disklabel == SUN_LABEL)
+ sun_set_pcylcount(cxt);
+ break;
+ default:
+ print_menu(EXPERT_MENU);
+ }
+ }
+}
+
+static int is_ide_cdrom_or_tape(char *device)
+{
+ int fd, ret;
+
+ if ((fd = open(device, O_RDONLY)) < 0)
+ return 0;
+ ret = blkdev_is_cdrom(fd);
+
+ close(fd);
+ return ret;
+}
+
+static void
+gpt_warning(char *dev)
+{
+ if (dev && gpt_probe_signature_devname(dev))
+ fprintf(stderr, _("\nWARNING: GPT (GUID Partition Table) detected on '%s'! "
+ "The util fdisk doesn't support GPT. Use GNU Parted.\n\n"), dev);
+}
+
+/* Print disk geometry and partition table of a specified device (-l option) */
+static void print_partition_table_from_option(char *device, unsigned long sector_size)
+{
+ struct fdisk_context *cxt;
+
+ cxt = fdisk_new_context_from_filename(device, 1); /* read-only */
+ if (!cxt)
+ err(EXIT_FAILURE, _("cannot open %s"), device);
+
+ if (sector_size) /* passed -b option, override autodiscovery */
+ fdisk_context_force_sector_size(cxt, sector_size);
+
+ if (user_cylinders || user_heads || user_sectors)
+ fdisk_context_set_user_geometry(cxt, user_cylinders,
+ user_heads, user_sectors);
+ gpt_warning(device);
+
+ if (!fdisk_dev_has_disklabel(cxt)) {
+ /*
+ * Try BSD -- TODO: move to list_table() too
+ */
+ list_disk_geometry(cxt);
+ if (disklabel != AIX_LABEL && disklabel != MAC_LABEL)
+ btrydev(cxt);
+ }
+ else
+ list_table(cxt, 0);
+
+ fdisk_free_context(cxt);
+ cxt = NULL;
+}
+
+/*
+ * for fdisk -l:
+ * try all things in /proc/partitions that look like a full disk
+ */
+static void
+print_all_partition_table_from_option(unsigned long sector_size)
+{
+ FILE *procpt;
+ char line[128 + 1], ptname[128 + 1], devname[256];
+ int ma, mi;
+ unsigned long long sz;
+
+ procpt = fopen(_PATH_PROC_PARTITIONS, "r");
+ if (procpt == NULL) {
+ fprintf(stderr, _("cannot open %s\n"), _PATH_PROC_PARTITIONS);
+ return;
+ }
+
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf (line, " %d %d %llu %128[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+ snprintf(devname, sizeof(devname), "/dev/%s", ptname);
+ if (is_whole_disk(devname)) {
+ char *cn = canonicalize_path(devname);
+ if (cn) {
+ if (!is_ide_cdrom_or_tape(cn))
+ print_partition_table_from_option(cn, sector_size);
+ free(cn);
+ }
+ }
+ }
+ fclose(procpt);
+}
+
+static void
+unknown_command(int c) {
+ printf(_("%c: unknown command\n"), c);
+}
+
+static void print_welcome(void)
+{
+ printf(_("Welcome to fdisk (%s).\n\n"
+ "Changes will remain in memory only, until you decide to write them.\n"
+ "Be careful before using the write command.\n\n"), PACKAGE_STRING);
+
+ fflush(stdout);
+}
+
+static void command_prompt(struct fdisk_context *cxt)
+{
+ int c;
+
+ if (disklabel == OSF_LABEL) {
+ putchar('\n');
+ /* OSF label, and no DOS label */
+ printf(_("Detected an OSF/1 disklabel on %s, entering "
+ "disklabel mode.\n"),
+ cxt->dev_path);
+ bsd_command_prompt(cxt);
+ /* If we return we may want to make an empty DOS label? */
+ disklabel = DOS_LABEL;
+ }
+
+ while (1) {
+ putchar('\n');
+ c = tolower(read_char(_("Command (m for help): ")));
+ switch (c) {
+ case 'a':
+ if (disklabel == DOS_LABEL)
+ toggle_active(get_partition(cxt, 1, partitions));
+ else if (disklabel == SUN_LABEL)
+ toggle_sunflags(cxt, get_partition(cxt, 1, partitions),
+ SUN_FLAG_UNMNT);
+ else if (disklabel == SGI_LABEL)
+ sgi_set_bootpartition(cxt,
+ get_partition(cxt, 1, partitions));
+ else
+ unknown_command(c);
+ break;
+ case 'b':
+ if (disklabel == SGI_LABEL)
+ sgi_set_bootfile(cxt);
+ else if (disklabel == DOS_LABEL) {
+ disklabel = OSF_LABEL;
+ bsd_command_prompt(cxt);
+ disklabel = DOS_LABEL;
+ } else
+ unknown_command(c);
+ break;
+ case 'c':
+ if (disklabel == DOS_LABEL)
+ toggle_dos_compatibility_flag(cxt);
+ else if (disklabel == SUN_LABEL)
+ toggle_sunflags(cxt, get_partition(cxt, 1, partitions),
+ SUN_FLAG_RONLY);
+ else if (disklabel == SGI_LABEL)
+ sgi_set_swappartition(cxt,
+ get_partition(cxt, 1, partitions));
+ else
+ unknown_command(c);
+ break;
+ case 'd':
+ delete_partition(cxt, get_existing_partition(cxt, 1, partitions));
+ break;
+ case 'i':
+ if (disklabel == SGI_LABEL)
+ create_sgiinfo(cxt);
+ else
+ unknown_command(c);
+ break;
+ case 'l':
+ list_types(get_sys_types());
+ break;
+ case 'm':
+ print_menu(MAIN_MENU);
+ break;
+ case 'n':
+ new_partition(cxt);
+ break;
+ case 'o':
+ fdisk_create_disklabel(cxt, "dos");
+ break;
+ case 'p':
+ list_table(cxt, 0);
+ break;
+ case 'q':
+ handle_quit(cxt);
+ case 's':
+ fdisk_create_disklabel(cxt, "sun");
+ break;
+ case 't':
+ change_sysid(cxt);
+ break;
+ case 'u':
+ change_units(cxt);
+ break;
+ case 'v':
+ verify(cxt);
+ break;
+ case 'w':
+ write_table(cxt);
+ break;
+ case 'x':
+ expert_command_prompt(cxt);
+ break;
+ default:
+ unknown_command(c);
+ print_menu(MAIN_MENU);
+ }
+ }
+}
+
+static sector_t get_dev_blocks(char *dev)
+{
+ int fd;
+ sector_t size;
+
+ if ((fd = open(dev, O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), dev);
+ if (blkdev_get_sectors(fd, &size) == -1) {
+ close(fd);
+ err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
+ }
+ close(fd);
+ return size/2;
+}
+
+int main(int argc, char **argv)
+{
+ int c, optl = 0, opts = 0;
+ unsigned long sector_size = 0;
+ struct fdisk_context *cxt = NULL;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ while ((c = getopt(argc, argv, "b:c::C:hH:lsS:u::vV")) != -1) {
+ switch (c) {
+ case 'b':
+ /* Ugly: this sector size is really per device,
+ so cannot be combined with multiple disks,
+ and te same goes for the C/H/S options.
+ */
+ sector_size = strtou32_or_err(optarg, _("invalid sector size argument"));
+ if (sector_size != 512 && sector_size != 1024 &&
+ sector_size != 2048 && sector_size != 4096)
+ usage(stderr);
+ sector_offset = 2;
+ break;
+ case 'C':
+ user_cylinders = strtou32_or_err(optarg, _("invalid cylinders argument"));
+ break;
+ case 'c':
+ dos_compatible_flag = 0; /* default */
+
+ if (optarg && !strcmp(optarg, "=dos"))
+ dos_compatible_flag = ~0;
+ else if (optarg && strcmp(optarg, "=nondos"))
+ usage(stderr);
+ break;
+ case 'H':
+ user_heads = strtou32_or_err(optarg, _("invalid heads argument"));
+ if (user_heads > 256)
+ user_heads = 0;
+ break;
+ case 'S':
+ user_sectors = strtou32_or_err(optarg, _("invalid sectors argument"));
+ if (user_sectors >= 64)
+ user_sectors = 0;
+ break;
+ case 'l':
+ optl = 1;
+ break;
+ case 's':
+ opts = 1;
+ break;
+ case 'u':
+ display_in_cyl_units = 0; /* default */
+ if (optarg && strcmp(optarg, "=cylinders") == 0)
+ display_in_cyl_units = !display_in_cyl_units;
+ else if (optarg && strcmp(optarg, "=sectors"))
+ usage(stderr);
+ break;
+ case 'V':
+ case 'v':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+
+ fdisk_init_debug(0);
+
+ if (sector_size && argc-optind != 1)
+ printf(_("Warning: the -b (set sector size) option should"
+ " be used with one specified device\n"));
+
+ if (optl) {
+ nowarn = 1;
+ if (argc > optind) {
+ int k;
+ for (k = optind; k < argc; k++)
+ print_partition_table_from_option(argv[k], sector_size);
+ } else
+ print_all_partition_table_from_option(sector_size);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (opts) {
+ /* print partition size for one or more devices */
+ int i, ndevs = argc - optind;
+ if (ndevs <= 0)
+ usage(stderr);
+
+ for (i = optind; i < argc; i++) {
+ if (ndevs == 1)
+ printf("%llu\n", get_dev_blocks(argv[i]));
+ else
+ printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i]));
+ }
+ exit(EXIT_SUCCESS);
+ }
+
+ if (argc-optind != 1)
+ usage(stderr);
+
+ cxt = fdisk_new_context_from_filename(argv[optind], 0);
+ if (!cxt)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
+
+ if (sector_size) /* passed -b option, override autodiscovery */
+ fdisk_context_force_sector_size(cxt, sector_size);
+
+ if (user_cylinders || user_heads || user_sectors)
+ fdisk_context_set_user_geometry(cxt, user_cylinders,
+ user_heads, user_sectors);
+
+ print_welcome();
+
+ if (!fdisk_dev_sectsz_is_default(cxt))
+ printf(_("Note: sector size is %ld (not %d)\n"),
+ cxt->sector_size, DEFAULT_SECTOR_SIZE);
+
+ gpt_warning(cxt->dev_path);
+
+ if (!fdisk_dev_has_disklabel(cxt)) {
+ fprintf(stderr,
+ _("Device does not contain a recognized partition table\n"));
+ fdisk_create_disklabel(cxt, NULL);
+ }
+
+ command_prompt(cxt);
+
+ return 0;
+}
diff --git a/fdisks/fdisk.h b/fdisks/fdisk.h
new file mode 100644
index 0000000..05dc8a8
--- /dev/null
+++ b/fdisks/fdisk.h
@@ -0,0 +1,294 @@
+/*
+ fdisk.h
+*/
+
+#include "c.h"
+
+#define DEFAULT_SECTOR_SIZE 512
+#define MAX_SECTOR_SIZE 2048
+#define SECTOR_SIZE 512 /* still used in BSD code */
+#define MAXIMUM_PARTS 60
+
+#define ACTIVE_FLAG 0x80
+
+#define EXTENDED 0x05
+#define WIN98_EXTENDED 0x0f
+#define LINUX_PARTITION 0x81
+#define LINUX_SWAP 0x82
+#define LINUX_NATIVE 0x83
+#define LINUX_EXTENDED 0x85
+#define LINUX_LVM 0x8e
+#define LINUX_RAID 0xfd
+
+#define ALIGN_UP 1
+#define ALIGN_DOWN 2
+#define ALIGN_NEAREST 3
+
+#define LINE_LENGTH 800
+
+#define IS_EXTENDED(i) \
+ ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+
+#define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
+#define scround(x) (((x)+units_per_sector-1)/units_per_sector)
+
+/* fdisk debugging flags/options */
+#define FDISK_DEBUG_INIT (1 << 1)
+#define FDISK_DEBUG_CONTEXT (1 << 2)
+#define FDISK_DEBUG_TOPOLOGY (1 << 3)
+#define FDISK_DEBUG_GEOMETRY (1 << 4)
+#define FDISK_DEBUG_LABEL (1 << 5)
+#define FDISK_DEBUG_ALL 0xFFFF
+
+# define ON_DBG(m, x) do { \
+ if ((FDISK_DEBUG_ ## m) & fdisk_debug_mask) { \
+ x; \
+ } \
+ } while (0)
+
+# define DBG(m, x) do { \
+ if ((FDISK_DEBUG_ ## m) & fdisk_debug_mask) { \
+ fprintf(stderr, "%d: fdisk: %8s: ", getpid(), # m); \
+ x; \
+ } \
+ } while (0)
+
+# define DBG_FLUSH do { \
+ if (fdisk_debug_mask && \
+ fdisk_debug_mask != FDISK_DEBUG_INIT) \
+ fflush(stderr); \
+ } while(0)
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+dbgprint(const char *mesg, ...)
+{
+ va_list ap;
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+extern int fdisk_debug_mask;
+extern void fdisk_init_debug(int mask);
+
+struct partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char head; /* starting head */
+ unsigned char sector; /* starting sector */
+ unsigned char cyl; /* starting cylinder */
+ unsigned char sys_ind; /* What partition type */
+ unsigned char end_head; /* end head */
+ unsigned char end_sector; /* end sector */
+ unsigned char end_cyl; /* end cylinder */
+ unsigned char start4[4]; /* starting sector counting from 0 */
+ unsigned char size4[4]; /* nr of sectors in partition */
+} __attribute__ ((packed));
+
+enum menutype {
+ MAIN_MENU,
+ EXPERT_MENU,
+};
+
+enum failure {
+ ioctl_error,
+ unable_to_read,
+ unable_to_seek,
+ unable_to_write
+};
+
+typedef unsigned long long sector_t;
+
+/*
+ * Legacy CHS based geometry
+ */
+struct fdisk_geometry {
+ unsigned int heads;
+ sector_t sectors;
+ sector_t cylinders;
+};
+
+struct fdisk_context {
+ int dev_fd; /* device descriptor */
+ char *dev_path; /* device path */
+ unsigned char *firstsector; /* buffer with master boot record */
+
+ /* topology */
+ unsigned long io_size; /* I/O size used by fdisk */
+ unsigned long optimal_io_size; /* optional I/O returned by device */
+ unsigned long min_io_size; /* minimal I/O size */
+ unsigned long phy_sector_size; /* physical size */
+ unsigned long sector_size; /* logical size */
+ unsigned long alignment_offset;
+
+ unsigned long grain; /* alignment unit */
+
+ /* geometry */
+ sector_t total_sectors; /* in logical sectors */
+ struct fdisk_geometry geom;
+
+ /* label operations and description */
+ const struct fdisk_label *label;
+};
+
+/*
+ * Label specific operations
+ */
+struct fdisk_label {
+ const char *name;
+
+ /* probe disk label */
+ int (*probe)(struct fdisk_context *cxt);
+ /* write in-memory changes to disk */
+ int (*write)(struct fdisk_context *cxt);
+ /* verify the partition table */
+ int (*verify)(struct fdisk_context *cxt);
+ /* create new disk label */
+ int (*create)(struct fdisk_context *cxt);
+ /* new partition */
+ void (*part_add)(struct fdisk_context *cxt, int partnum, int parttype);
+ /* delete partition */
+ void (*part_delete)(struct fdisk_context *cxt, int partnum);
+};
+
+/*
+ * labels
+ */
+extern const struct fdisk_label aix_label;
+extern const struct fdisk_label dos_label;
+extern const struct fdisk_label bsd_label;
+extern const struct fdisk_label mac_label;
+extern const struct fdisk_label sun_label;
+extern const struct fdisk_label sgi_label;
+
+extern struct fdisk_context *fdisk_new_context_from_filename(const char *fname, int readonly);
+extern int fdisk_dev_has_topology(struct fdisk_context *cxt);
+extern int fdisk_dev_has_disklabel(struct fdisk_context *cxt);
+extern int fdisk_dev_sectsz_is_default(struct fdisk_context *cxt);
+extern void fdisk_free_context(struct fdisk_context *cxt);
+extern void fdisk_zeroize_firstsector(struct fdisk_context *cxt);
+extern int fdisk_context_force_sector_size(struct fdisk_context *cxt, sector_t s);
+extern int fdisk_context_set_user_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders, unsigned int heads,
+ unsigned int sectors);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, int partnum);
+extern int fdisk_add_partition(struct fdisk_context *cxt, int partnum, int parttype);
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+
+/* prototypes for fdisk.c */
+extern char *line_ptr;
+extern int partitions;
+extern unsigned int display_in_cyl_units, units_per_sector;
+
+extern void check_consistency(struct fdisk_context *cxt, struct partition *p, int partition);
+extern void check_alignment(struct fdisk_context *cxt, sector_t lba, int partition);
+extern void check(struct fdisk_context *cxt, int n, unsigned int h, unsigned int s, unsigned int c, unsigned int start);
+
+extern void change_units(struct fdisk_context *cxt);
+extern void fatal(struct fdisk_context *cxt, enum failure why);
+extern int get_partition(struct fdisk_context *cxt, int warn, int max);
+extern void list_types(struct systypes *sys);
+extern int read_line (int *asked);
+extern char read_char(char *mesg);
+extern int read_hex(struct systypes *sys);
+extern void reread_partition_table(struct fdisk_context *cxt, int leave);
+extern struct partition *get_part_table(int);
+extern unsigned int read_int(struct fdisk_context *cxt,
+ unsigned int low, unsigned int dflt,
+ unsigned int high, unsigned int base, char *mesg);
+extern void print_menu(enum menutype);
+extern void print_partition_size(struct fdisk_context *cxt, int num, sector_t start, sector_t stop, int sysid);
+
+extern void fill_bounds(sector_t *first, sector_t *last);
+
+extern char *partition_type(unsigned char type);
+extern void update_units(struct fdisk_context *cxt);
+extern char read_chars(char *mesg);
+extern void set_changed(int);
+extern void set_all_unchanged(void);
+extern int warn_geometry(struct fdisk_context *cxt);
+extern void warn_limits(struct fdisk_context *cxt);
+extern void warn_alignment(struct fdisk_context *cxt);
+extern unsigned int read_int_with_suffix(struct fdisk_context *cxt,
+ unsigned int low, unsigned int dflt, unsigned int high,
+ unsigned int base, char *mesg, int *is_suffix_used);
+extern sector_t align_lba(struct fdisk_context *cxt, sector_t lba, int direction);
+extern int get_partition_dflt(struct fdisk_context *cxt, int warn, int max, int dflt);
+extern void update_sector_offset(struct fdisk_context *cxt);
+
+#define PLURAL 0
+#define SINGULAR 1
+extern const char * str_units(int);
+
+extern sector_t get_nr_sects(struct partition *p);
+
+/*
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+ DOS_LABEL = 1,
+ SUN_LABEL = 2,
+ SGI_LABEL = 4,
+ AIX_LABEL = 8,
+ OSF_LABEL = 16,
+ MAC_LABEL = 32,
+ ANY_LABEL = -1
+};
+
+extern enum fdisk_labeltype disklabel;
+extern int MBRbuffer_changed;
+
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static inline void
+store4_little_endian(unsigned char *cp, unsigned int val) {
+ cp[0] = (val & 0xff);
+ cp[1] = ((val >> 8) & 0xff);
+ cp[2] = ((val >> 16) & 0xff);
+ cp[3] = ((val >> 24) & 0xff);
+}
+
+static inline unsigned int read4_little_endian(const unsigned char *cp)
+{
+ return (unsigned int)(cp[0]) + ((unsigned int)(cp[1]) << 8)
+ + ((unsigned int)(cp[2]) << 16)
+ + ((unsigned int)(cp[3]) << 24);
+}
+
+static inline void set_nr_sects(struct partition *p, sector_t nr_sects)
+{
+ store4_little_endian(p->size4, nr_sects);
+}
+
+static inline void set_start_sect(struct partition *p, unsigned int start_sect)
+{
+ store4_little_endian(p->start4, start_sect);
+}
+
+static inline void seek_sector(struct fdisk_context *cxt, sector_t secno)
+{
+ off_t offset = (off_t) secno * cxt->sector_size;
+ if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+ fatal(cxt, unable_to_seek);
+}
+
+static inline void read_sector(struct fdisk_context *cxt, sector_t secno, unsigned char *buf)
+{
+ seek_sector(cxt, secno);
+ if (read(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
+ fatal(cxt, unable_to_read);
+}
+
+static inline sector_t get_start_sect(struct partition *p)
+{
+ return read4_little_endian(p->start4);
+}
+
+static inline int is_cleared_partition(struct partition *p)
+{
+ return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
+ p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
+ get_start_sect(p) || get_nr_sects(p));
+}
diff --git a/fdisks/fdiskaixlabel.c b/fdisks/fdiskaixlabel.c
new file mode 100644
index 0000000..6fe7b59
--- /dev/null
+++ b/fdisks/fdiskaixlabel.c
@@ -0,0 +1,90 @@
+/*
+ Changes:
+ Sat Mar 20 09:51:38 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Internationalization
+*/
+#include <stdio.h> /* stderr */
+#include <string.h> /* strstr */
+#include <unistd.h> /* write */
+
+#include <endian.h>
+
+#include "common.h"
+#include "fdisk.h"
+#include "fdiskaixlabel.h"
+#include "nls.h"
+
+static int other_endian = 0;
+static short volumes=1;
+
+/*
+ * only dealing with free blocks here
+ */
+
+static void
+aix_info( void ) {
+ puts(
+ _("\n\tThere is a valid AIX label on this disk.\n"
+ "\tUnfortunately Linux cannot handle these\n"
+ "\tdisks at the moment. Nevertheless some\n"
+ "\tadvice:\n"
+ "\t1. fdisk will destroy its contents on write.\n"
+ "\t2. Be sure that this disk is NOT a still vital\n"
+ "\t part of a volume group. (Otherwise you may\n"
+ "\t erase the other disks as well, if unmirrored.)\n"
+ "\t3. Before deleting this physical volume be sure\n"
+ "\t to remove the disk logically from your AIX\n"
+ "\t machine. (Otherwise you become an AIXpert).")
+ );
+}
+
+static void aix_nolabel(struct fdisk_context *cxt)
+{
+ struct aix_partition *aixlabel = (struct aix_partition *) cxt->firstsector;
+
+ aixlabel->magic = 0;
+ partitions = 4;
+ fdisk_zeroize_firstsector(cxt);
+ return;
+}
+
+static int aix_probe_label(struct fdisk_context *cxt)
+{
+ struct aix_partition *aixlabel = (struct aix_partition *) cxt->firstsector;
+
+ if (aixlabel->magic != AIX_LABEL_MAGIC &&
+ aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) {
+ other_endian = 0;
+ return 0;
+ }
+ other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
+ disklabel = AIX_LABEL;
+ partitions= 1016;
+ volumes = 15;
+ aix_info();
+ aix_nolabel(cxt); /* %% */
+ return 1;
+}
+
+static void aix_add_partition(
+ struct fdisk_context *cxt __attribute__((__unused__)),
+ int partnum __attribute__((__unused__)),
+ int parttype __attribute__((__unused__)))
+{
+ printf(_("\tSorry - this fdisk cannot handle AIX disk labels."
+ "\n\tIf you want to add DOS-type partitions, create"
+ "\n\ta new empty DOS partition table first. (Use o.)"
+ "\n\tWARNING: "
+ "This will destroy the present disk contents.\n"));
+}
+
+const struct fdisk_label aix_label =
+{
+ .name = "aix",
+ .probe = aix_probe_label,
+ .write = NULL,
+ .verify = NULL,
+ .create = NULL,
+ .part_add = aix_add_partition,
+ .part_delete = NULL,
+};
diff --git a/fdisks/fdiskaixlabel.h b/fdisks/fdiskaixlabel.h
new file mode 100644
index 0000000..c3af9a0
--- /dev/null
+++ b/fdisks/fdiskaixlabel.h
@@ -0,0 +1,25 @@
+#ifndef FDISK_AIX_LABEL_H
+#define FDISK_AIX_LABEL_H
+
+#include <stdint.h>
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be redistributed under
+ * the terms of the GNU Public License.
+ */
+
+struct aix_partition {
+ unsigned int magic; /* expect AIX_LABEL_MAGIC */
+ unsigned int fillbytes1[124];
+ unsigned int physical_volume_id;
+ unsigned int fillbytes2[124];
+};
+
+#define AIX_LABEL_MAGIC 0xc9c2d4c1
+#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9
+#define AIX_INFO_MAGIC 0x00072959
+#define AIX_INFO_MAGIC_SWAPPED 0x59290700
+
+/* fdiskaixlabel.c */
+extern struct systypes aix_sys_types[];
+#endif /* FDISK_AIX_LABEL_H */
diff --git a/fdisks/fdiskbsdlabel.c b/fdisks/fdiskbsdlabel.c
new file mode 100644
index 0000000..442a79f
--- /dev/null
+++ b/fdisks/fdiskbsdlabel.c
@@ -0,0 +1,855 @@
+/*
+ NetBSD disklabel editor for Linux fdisk
+ Written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de)
+ with code from the NetBSD disklabel command:
+
+ Copyright (c) 1987, 1988 Regents of the University of California.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors.
+ 4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ Changes:
+ 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
+
+ 20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
+ support for OSF/1 disklabels on Alpha.
+ Also fixed unaligned accesses in alpha_bootblock_checksum()
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <errno.h>
+#include "nls.h"
+
+#include <sys/param.h>
+
+#include "common.h"
+#include "fdisk.h"
+#define FREEBSD_PARTITION 0xa5
+#define NETBSD_PARTITION 0xa9
+#define DKTYPENAMES
+#include "fdiskbsdlabel.h"
+
+static void xbsd_delete_part (struct fdisk_context *cxt, int partnum);
+static void xbsd_edit_disklabel (void);
+static void xbsd_write_bootstrap (struct fdisk_context *cxt);
+static void xbsd_change_fstype (void);
+static int xbsd_get_part_index (int max);
+static int xbsd_check_new_partition (int *i);
+static void xbsd_list_types (void);
+static unsigned short xbsd_dkcksum (struct xbsd_disklabel *lp);
+static int xbsd_initlabel (struct fdisk_context *cxt,
+ struct partition *p, struct xbsd_disklabel *d,
+ int pindex);
+static int xbsd_readlabel (struct fdisk_context *cxt,
+ struct partition *p, struct xbsd_disklabel *d);
+static int xbsd_writelabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d);
+static void sync_disks (void);
+
+#if defined (__alpha__)
+void alpha_bootblock_checksum (char *boot);
+#endif
+
+#if !defined (__alpha__)
+static int xbsd_translate_fstype (int linux_type);
+static void xbsd_link_part (struct fdisk_context *cxt);
+static struct partition *xbsd_part;
+static int xbsd_part_index;
+#endif
+
+#if defined (__alpha__)
+/* We access this through a u_int64_t * when checksumming */
+static char disklabelbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
+#else
+static char disklabelbuffer[BSD_BBSIZE];
+#endif
+
+static struct xbsd_disklabel xbsd_dlabel;
+
+#define bsd_cround(n) \
+ (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
+
+/*
+ * Test whether the whole disk has BSD disk label magic.
+ *
+ * Note: often reformatting with DOS-type label leaves the BSD magic,
+ * so this does not mean that there is a BSD disk label.
+ */
+static int
+osf_probe_label(struct fdisk_context *cxt) {
+ if (xbsd_readlabel (cxt, NULL, &xbsd_dlabel) == 0)
+ return 0;
+ return 1;
+}
+
+int
+btrydev (struct fdisk_context *cxt) {
+ if (xbsd_readlabel (cxt, NULL, &xbsd_dlabel) == 0)
+ return -1;
+ printf(_("\nBSD label for device: %s\n"), cxt->dev_path);
+ xbsd_print_disklabel (cxt, 0);
+ return 0;
+}
+
+#if !defined (__alpha__)
+static int
+hidden(int type) {
+ return type ^ 0x10;
+}
+
+static int
+is_bsd_partition_type(int type) {
+ return (type == FREEBSD_PARTITION ||
+ type == hidden(FREEBSD_PARTITION) ||
+ type == NETBSD_PARTITION ||
+ type == hidden(NETBSD_PARTITION));
+}
+#endif
+
+static int xbsd_write_disklabel (struct fdisk_context *cxt)
+{
+#if defined (__alpha__)
+ printf (_("Writing disklabel to %s.\n"), cxt->dev_path);
+ xbsd_writelabel (cxt, NULL, &xbsd_dlabel);
+#else
+ printf (_("Writing disklabel to %s.\n"),
+ partname(cxt->dev_path, xbsd_part_index+1, 0));
+ xbsd_writelabel (cxt, xbsd_part, &xbsd_dlabel);
+#endif
+ reread_partition_table(cxt, 0); /* no exit yet */
+
+ return 0;
+}
+
+static void xbsd_add_part (struct fdisk_context *cxt,
+ int partnum __attribute__((__unused__)),
+ int parttype __attribute__((__unused__)))
+{
+ unsigned int begin, end;
+ char mesg[256];
+ int i;
+
+ if (!xbsd_check_new_partition (&i))
+ return;
+
+#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
+ begin = get_start_sect(xbsd_part);
+ end = begin + get_nr_sects(xbsd_part) - 1;
+#else
+ begin = 0;
+ end = xbsd_dlabel.d_secperunit - 1;
+#endif
+
+ snprintf (mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+ begin = read_int (cxt, bsd_cround (begin), bsd_cround (begin), bsd_cround (end),
+ 0, mesg);
+
+ if (display_in_cyl_units)
+ begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
+
+ snprintf (mesg, sizeof(mesg), _("Last %s or +size or +sizeM or +sizeK"),
+ str_units(SINGULAR));
+ end = read_int (cxt, bsd_cround (begin), bsd_cround (end), bsd_cround (end),
+ bsd_cround (begin), mesg);
+
+ if (display_in_cyl_units)
+ end = end * xbsd_dlabel.d_secpercyl - 1;
+
+ xbsd_dlabel.d_partitions[i].p_size = end - begin + 1;
+ xbsd_dlabel.d_partitions[i].p_offset = begin;
+ xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+}
+
+static int xbsd_create_disklabel (struct fdisk_context *cxt)
+{
+ char c;
+
+#if defined (__alpha__)
+ fprintf (stderr, _("%s contains no disklabel.\n"), cxt->dev_path);
+#else
+ fprintf (stderr, _("%s contains no disklabel.\n"),
+ partname(cxt->dev_path, xbsd_part_index+1, 0));
+#endif
+
+ while (1) {
+ c = read_char (_("Do you want to create a disklabel? (y/n) "));
+ if (tolower(c) == 'y') {
+ if (xbsd_initlabel (cxt,
+#if defined (__alpha__) || defined (__powerpc__) || defined (__hppa__) || \
+ defined (__s390__) || defined (__s390x__)
+ NULL, &xbsd_dlabel, 0
+#else
+ xbsd_part, &xbsd_dlabel, xbsd_part_index
+#endif
+ ) == 1) {
+ xbsd_print_disklabel (cxt, 1);
+ return 1;
+ } else
+ return 0;
+ } else if (c == 'n')
+ return 0;
+ }
+}
+
+void
+bsd_command_prompt (struct fdisk_context *cxt)
+{
+#if !defined (__alpha__)
+ int t, ss;
+ struct partition *p;
+
+ for (t=0; t<4; t++) {
+ p = get_part_table(t);
+ if (p && is_bsd_partition_type(p->sys_ind)) {
+ xbsd_part = p;
+ xbsd_part_index = t;
+ ss = get_start_sect(xbsd_part);
+ if (ss == 0) {
+ fprintf (stderr, _("Partition %s has invalid starting sector 0.\n"),
+ partname(cxt->dev_path, t+1, 0));
+ return;
+ }
+ printf (_("Reading disklabel of %s at sector %d.\n"),
+ partname(cxt->dev_path, t+1, 0), ss + BSD_LABELSECTOR);
+ if (xbsd_readlabel (cxt, xbsd_part, &xbsd_dlabel) == 0)
+ if (xbsd_create_disklabel (cxt) == 0)
+ return;
+ break;
+ }
+ }
+
+ if (t == 4) {
+ printf (_("There is no *BSD partition on %s.\n"), cxt->dev_path);
+ return;
+ }
+
+#elif defined (__alpha__)
+
+ if (xbsd_readlabel (cxt, NULL, &xbsd_dlabel) == 0)
+ if (xbsd_create_disklabel (cxt) == 0)
+ exit ( EXIT_SUCCESS );
+
+#endif
+
+ while (1) {
+ putchar ('\n');
+ switch (tolower (read_char (_("BSD disklabel command (m for help): ")))) {
+ case 'd':
+ xbsd_delete_part(cxt, xbsd_get_part_index(xbsd_dlabel.d_npartitions));
+ break;
+ case 'e':
+ xbsd_edit_disklabel ();
+ break;
+ case 'i':
+ xbsd_write_bootstrap (cxt);
+ break;
+ case 'l':
+ xbsd_list_types ();
+ break;
+ case 'n':
+ xbsd_add_part (cxt, 0, 0);
+ break;
+ case 'p':
+ xbsd_print_disklabel (cxt, 0);
+ break;
+ case 'q':
+ close (cxt->dev_fd);
+ exit ( EXIT_SUCCESS );
+ case 'r':
+ return;
+ case 's':
+ xbsd_print_disklabel (cxt, 1);
+ break;
+ case 't':
+ xbsd_change_fstype ();
+ break;
+ case 'u':
+ change_units(cxt);
+ break;
+ case 'w':
+ xbsd_write_disklabel (cxt);
+ break;
+#if !defined (__alpha__)
+ case 'x':
+ xbsd_link_part (cxt);
+ break;
+#endif
+ default:
+ print_menu(MAIN_MENU);
+ break;
+ }
+ }
+}
+
+static void xbsd_delete_part(struct fdisk_context *cxt __attribute__((__unused__)),
+ int partnum)
+{
+ xbsd_dlabel.d_partitions[partnum].p_size = 0;
+ xbsd_dlabel.d_partitions[partnum].p_offset = 0;
+ xbsd_dlabel.d_partitions[partnum].p_fstype = BSD_FS_UNUSED;
+ if (xbsd_dlabel.d_npartitions == partnum + 1)
+ while (!xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size)
+ xbsd_dlabel.d_npartitions--;
+}
+
+void
+xbsd_print_disklabel (struct fdisk_context *cxt, int show_all) {
+ struct xbsd_disklabel *lp = &xbsd_dlabel;
+ struct xbsd_partition *pp;
+ FILE *f = stdout;
+ int i, j;
+
+ if (show_all) {
+#if defined (__alpha__)
+ fprintf(f, "# %s:\n", cxt->dev_path);
+#else
+ fprintf(f, "# %s:\n", partname(cxt->dev_path, xbsd_part_index+1, 0));
+#endif
+ if ((unsigned) lp->d_type < BSD_DKMAXTYPES)
+ fprintf(f, _("type: %s\n"), xbsd_dktypenames[lp->d_type]);
+ else
+ fprintf(f, _("type: %d\n"), lp->d_type);
+ fprintf(f, _("disk: %.*s\n"), (int) sizeof(lp->d_typename), lp->d_typename);
+ fprintf(f, _("label: %.*s\n"), (int) sizeof(lp->d_packname), lp->d_packname);
+ fprintf(f, _("flags:"));
+ if (lp->d_flags & BSD_D_REMOVABLE)
+ fprintf(f, _(" removable"));
+ if (lp->d_flags & BSD_D_ECC)
+ fprintf(f, _(" ecc"));
+ if (lp->d_flags & BSD_D_BADSECT)
+ fprintf(f, _(" badsect"));
+ fprintf(f, "\n");
+ /* On various machines the fields of *lp are short/int/long */
+ /* In order to avoid problems, we cast them all to long. */
+ fprintf(f, _("bytes/sector: %ld\n"), (long) lp->d_secsize);
+ fprintf(f, _("sectors/track: %ld\n"), (long) lp->d_nsectors);
+ fprintf(f, _("tracks/cylinder: %ld\n"), (long) lp->d_ntracks);
+ fprintf(f, _("sectors/cylinder: %ld\n"), (long) lp->d_secpercyl);
+ fprintf(f, _("cylinders: %ld\n"), (long) lp->d_ncylinders);
+ fprintf(f, _("rpm: %d\n"), lp->d_rpm);
+ fprintf(f, _("interleave: %d\n"), lp->d_interleave);
+ fprintf(f, _("trackskew: %d\n"), lp->d_trackskew);
+ fprintf(f, _("cylinderskew: %d\n"), lp->d_cylskew);
+ fprintf(f, _("headswitch: %ld\t\t# milliseconds\n"),
+ (long) lp->d_headswitch);
+ fprintf(f, _("track-to-track seek: %ld\t# milliseconds\n"),
+ (long) lp->d_trkseek);
+ fprintf(f, _("drivedata: "));
+ for (i = NDDATA - 1; i >= 0; i--)
+ if (lp->d_drivedata[i])
+ break;
+ if (i < 0)
+ i = 0;
+ for (j = 0; j <= i; j++)
+ fprintf(f, "%ld ", (long) lp->d_drivedata[j]);
+ }
+ fprintf (f, _("\n%d partitions:\n"), lp->d_npartitions);
+ fprintf (f, _("# start end size fstype [fsize bsize cpg]\n"));
+ pp = lp->d_partitions;
+ for (i = 0; i < lp->d_npartitions; i++, pp++) {
+ if (pp->p_size) {
+ if (display_in_cyl_units && lp->d_secpercyl) {
+ fprintf(f, " %c: %8ld%c %8ld%c %8ld%c ",
+ 'a' + i,
+ (long) pp->p_offset / lp->d_secpercyl + 1,
+ (pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
+ (long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1)
+ / lp->d_secpercyl,
+ ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
+ (long) pp->p_size / lp->d_secpercyl,
+ (pp->p_size % lp->d_secpercyl) ? '*' : ' ');
+ } else {
+ fprintf(f, " %c: %8ld %8ld %8ld ",
+ 'a' + i,
+ (long) pp->p_offset,
+ (long) pp->p_offset + pp->p_size - 1,
+ (long) pp->p_size);
+ }
+ if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES)
+ fprintf(f, "%8.8s", xbsd_fstypes[pp->p_fstype].name);
+ else
+ fprintf(f, "%8x", pp->p_fstype);
+ switch (pp->p_fstype) {
+ case BSD_FS_UNUSED:
+ fprintf(f, " %5ld %5ld %5.5s ",
+ (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
+ break;
+
+ case BSD_FS_BSDFFS:
+ fprintf(f, " %5ld %5ld %5d ",
+ (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag,
+ pp->p_cpg);
+ break;
+
+ default:
+ fprintf(f, "%22.22s", "");
+ break;
+ }
+ fprintf(f, "\n");
+ }
+ }
+}
+
+static int
+edit_int (int def, char *mesg)
+{
+ do {
+ fputs (mesg, stdout);
+ printf (" (%d): ", def);
+ if (!read_line (NULL))
+ return def;
+ }
+ while (!isdigit (*line_ptr));
+ return atoi (line_ptr);
+}
+
+static void
+xbsd_edit_disklabel (void)
+{
+ struct xbsd_disklabel *d;
+
+ d = &xbsd_dlabel;
+
+#if defined (__alpha__) || defined (__ia64__)
+ d -> d_secsize = (unsigned long) edit_int ((unsigned long) d -> d_secsize ,_("bytes/sector"));
+ d -> d_nsectors = (unsigned long) edit_int ((unsigned long) d -> d_nsectors ,_("sectors/track"));
+ d -> d_ntracks = (unsigned long) edit_int ((unsigned long) d -> d_ntracks ,_("tracks/cylinder"));
+ d -> d_ncylinders = (unsigned long) edit_int ((unsigned long) d -> d_ncylinders ,_("cylinders"));
+#endif
+
+ /* d -> d_secpercyl can be != d -> d_nsectors * d -> d_ntracks */
+ while (1)
+ {
+ d -> d_secpercyl = (unsigned long) edit_int ((unsigned long) d -> d_nsectors * d -> d_ntracks,
+ _("sectors/cylinder"));
+ if (d -> d_secpercyl <= d -> d_nsectors * d -> d_ntracks)
+ break;
+
+ printf (_("Must be <= sectors/track * tracks/cylinder (default).\n"));
+ }
+ d -> d_rpm = (unsigned short) edit_int ((unsigned short) d -> d_rpm ,_("rpm"));
+ d -> d_interleave = (unsigned short) edit_int ((unsigned short) d -> d_interleave,_("interleave"));
+ d -> d_trackskew = (unsigned short) edit_int ((unsigned short) d -> d_trackskew ,_("trackskew"));
+ d -> d_cylskew = (unsigned short) edit_int ((unsigned short) d -> d_cylskew ,_("cylinderskew"));
+ d -> d_headswitch = (unsigned long) edit_int ((unsigned long) d -> d_headswitch ,_("headswitch"));
+ d -> d_trkseek = (unsigned long) edit_int ((unsigned long) d -> d_trkseek ,_("track-to-track seek"));
+
+ d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+}
+
+static int
+xbsd_get_bootstrap (char *path, void *ptr, int size)
+{
+ int fd;
+
+ if ((fd = open (path, O_RDONLY)) < 0)
+ {
+ perror (path);
+ return 0;
+ }
+ if (read (fd, ptr, size) < 0)
+ {
+ perror (path);
+ close (fd);
+ return 0;
+ }
+ printf (" ... %s\n", path);
+ close (fd);
+ return 1;
+}
+
+static void
+xbsd_write_bootstrap (struct fdisk_context *cxt)
+{
+ char *bootdir = BSD_LINUX_BOOTDIR;
+ char path[sizeof(BSD_LINUX_BOOTDIR) + 1 + 2 + 4]; /* BSD_LINUX_BOOTDIR + / + {sd,wd} + boot */
+ char *dkbasename;
+ struct xbsd_disklabel dl;
+ char *d, *p, *e;
+ int sector;
+
+ if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
+ dkbasename = "sd";
+ else
+ dkbasename = "wd";
+
+ printf (_("Bootstrap: %sboot -> boot%s (%s): "),
+ dkbasename, dkbasename, dkbasename);
+ if (read_line (NULL)) {
+ line_ptr[strlen (line_ptr)-1] = '\0';
+ dkbasename = line_ptr;
+ }
+ snprintf (path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
+ if (!xbsd_get_bootstrap (path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
+ return;
+
+ /* We need a backup of the disklabel (xbsd_dlabel might have changed). */
+ d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
+ memmove (&dl, d, sizeof (struct xbsd_disklabel));
+
+ /* The disklabel will be overwritten by 0's from bootxx anyway */
+ memset (d, 0, sizeof (struct xbsd_disklabel));
+
+ snprintf (path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
+ if (!xbsd_get_bootstrap (path, &disklabelbuffer[xbsd_dlabel.d_secsize],
+ (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
+ return;
+
+ e = d + sizeof (struct xbsd_disklabel);
+ for (p=d; p < e; p++)
+ if (*p) {
+ fprintf (stderr, _("Bootstrap overlaps with disk label!\n"));
+ exit ( EXIT_FAILURE );
+ }
+
+ memmove (d, &dl, sizeof (struct xbsd_disklabel));
+
+#if defined (__powerpc__) || defined (__hppa__)
+ sector = 0;
+#elif defined (__alpha__)
+ sector = 0;
+ alpha_bootblock_checksum (disklabelbuffer);
+#else
+ sector = get_start_sect(xbsd_part);
+#endif
+
+ if (lseek (cxt->dev_fd, (off_t) sector * SECTOR_SIZE, SEEK_SET) == -1)
+ fatal (cxt, unable_to_seek);
+ if (BSD_BBSIZE != write (cxt->dev_fd, disklabelbuffer, BSD_BBSIZE))
+ fatal (cxt, unable_to_write);
+
+#if defined (__alpha__)
+ printf (_("Bootstrap installed on %s.\n"), cxt->dev_path);
+#else
+ printf (_("Bootstrap installed on %s.\n"),
+ partname (cxt->dev_path, xbsd_part_index+1, 0));
+#endif
+
+ sync_disks ();
+}
+
+static void
+xbsd_change_fstype (void)
+{
+ int i;
+
+ i = xbsd_get_part_index (xbsd_dlabel.d_npartitions);
+ xbsd_dlabel.d_partitions[i].p_fstype = read_hex (xbsd_fstypes);
+}
+
+static int
+xbsd_get_part_index (int max)
+{
+ char prompt[256];
+ char l;
+
+ snprintf (prompt, sizeof(prompt), _("Partition (a-%c): "), 'a' + max - 1);
+ do
+ l = tolower (read_char (prompt));
+ while (l < 'a' || l > 'a' + max - 1);
+ return l - 'a';
+}
+
+static int
+xbsd_check_new_partition (int *i) {
+
+ /* room for more? various BSD flavours have different maxima */
+ if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
+ int t;
+
+ for (t = 0; t < BSD_MAXPARTITIONS; t++)
+ if (xbsd_dlabel.d_partitions[t].p_size == 0)
+ break;
+
+ if (t == BSD_MAXPARTITIONS) {
+ fprintf (stderr, _("The maximum number of partitions "
+ "has been created\n"));
+ return 0;
+ }
+ }
+
+ *i = xbsd_get_part_index (BSD_MAXPARTITIONS);
+
+ if (*i >= xbsd_dlabel.d_npartitions)
+ xbsd_dlabel.d_npartitions = (*i) + 1;
+
+ if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
+ fprintf (stderr, _("This partition already exists.\n"));
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+xbsd_list_types (void) {
+ list_types (xbsd_fstypes);
+}
+
+static unsigned short
+xbsd_dkcksum (struct xbsd_disklabel *lp) {
+ unsigned short *start, *end;
+ unsigned short sum = 0;
+
+ start = (unsigned short *) lp;
+ end = (unsigned short *) &lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return sum;
+}
+
+static int
+xbsd_initlabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d,
+ int pindex __attribute__((__unused__))) {
+ struct xbsd_partition *pp;
+
+ memset (d, 0, sizeof (struct xbsd_disklabel));
+
+ d -> d_magic = BSD_DISKMAGIC;
+
+ if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0)
+ d -> d_type = BSD_DTYPE_SCSI;
+ else
+ d -> d_type = BSD_DTYPE_ST506;
+
+#if 0 /* not used (at least not written to disk) by NetBSD/i386 1.0 */
+ d -> d_subtype = BSD_DSTYPE_INDOSPART & pindex;
+#endif
+
+#if !defined (__alpha__)
+ d -> d_flags = BSD_D_DOSPART;
+#else
+ d -> d_flags = 0;
+#endif
+ d -> d_secsize = SECTOR_SIZE; /* bytes/sector */
+ d -> d_nsectors = cxt->geom.sectors; /* sectors/track */
+ d -> d_ntracks = cxt->geom.heads; /* tracks/cylinder (heads) */
+ d -> d_ncylinders = cxt->geom.cylinders;
+ d -> d_secpercyl = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */
+ if (d -> d_secpercyl == 0)
+ d -> d_secpercyl = 1; /* avoid segfaults */
+ d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+
+ d -> d_rpm = 3600;
+ d -> d_interleave = 1;
+ d -> d_trackskew = 0;
+ d -> d_cylskew = 0;
+ d -> d_headswitch = 0;
+ d -> d_trkseek = 0;
+
+ d -> d_magic2 = BSD_DISKMAGIC;
+ d -> d_bbsize = BSD_BBSIZE;
+ d -> d_sbsize = BSD_SBSIZE;
+
+#if !defined (__alpha__)
+ d -> d_npartitions = 4;
+ pp = &d -> d_partitions[2]; /* Partition C should be
+ the NetBSD partition */
+ pp -> p_offset = get_start_sect(p);
+ pp -> p_size = get_nr_sects(p);
+ pp -> p_fstype = BSD_FS_UNUSED;
+ pp = &d -> d_partitions[3]; /* Partition D should be
+ the whole disk */
+ pp -> p_offset = 0;
+ pp -> p_size = d -> d_secperunit;
+ pp -> p_fstype = BSD_FS_UNUSED;
+#elif defined (__alpha__)
+ d -> d_npartitions = 3;
+ pp = &d -> d_partitions[2]; /* Partition C should be
+ the whole disk */
+ pp -> p_offset = 0;
+ pp -> p_size = d -> d_secperunit;
+ pp -> p_fstype = BSD_FS_UNUSED;
+#endif
+
+ return 1;
+}
+
+/*
+ * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 1.
+ */
+static int
+xbsd_readlabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d)
+{
+ int t, sector;
+
+ /* p is used only to get the starting sector */
+#if !defined (__alpha__)
+ sector = (p ? get_start_sect(p) : 0);
+#elif defined (__alpha__)
+ sector = 0;
+#endif
+
+ if (lseek (cxt->dev_fd, (off_t) sector * SECTOR_SIZE, SEEK_SET) == -1)
+ fatal (cxt, unable_to_seek);
+ if (BSD_BBSIZE != read (cxt->dev_fd, disklabelbuffer, BSD_BBSIZE))
+ fatal (cxt, unable_to_read);
+
+ memmove (d,
+ &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+ sizeof (struct xbsd_disklabel));
+
+ if (d -> d_magic != BSD_DISKMAGIC || d -> d_magic2 != BSD_DISKMAGIC)
+ return 0;
+
+ for (t = d -> d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+ d -> d_partitions[t].p_size = 0;
+ d -> d_partitions[t].p_offset = 0;
+ d -> d_partitions[t].p_fstype = BSD_FS_UNUSED;
+ }
+
+ if (d -> d_npartitions > BSD_MAXPARTITIONS)
+ fprintf (stderr, _("Warning: too many partitions "
+ "(%d, maximum is %d).\n"),
+ d -> d_npartitions, BSD_MAXPARTITIONS);
+ return 1;
+}
+
+static int
+xbsd_writelabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d)
+{
+ unsigned int sector;
+
+#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
+ sector = get_start_sect(p) + BSD_LABELSECTOR;
+#else
+ sector = BSD_LABELSECTOR;
+#endif
+
+ d -> d_checksum = 0;
+ d -> d_checksum = xbsd_dkcksum (d);
+
+ /* This is necessary if we want to write the bootstrap later,
+ otherwise we'd write the old disklabel with the bootstrap.
+ */
+ memmove (&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], d,
+ sizeof (struct xbsd_disklabel));
+
+#if defined (__alpha__) && BSD_LABELSECTOR == 0
+ alpha_bootblock_checksum (disklabelbuffer);
+ if (lseek (cxt->dev_fd, (off_t) 0, SEEK_SET) == -1)
+ fatal (cxt, unable_to_seek);
+ if (BSD_BBSIZE != write (cxt->dev_fd, disklabelbuffer, BSD_BBSIZE))
+ fatal (cxt, unable_to_write);
+#else
+ if (lseek (cxt->dev_fd, (off_t) sector * SECTOR_SIZE + BSD_LABELOFFSET,
+ SEEK_SET) == -1)
+ fatal (cxt, unable_to_seek);
+ if (sizeof (struct xbsd_disklabel) != write (cxt->dev_fd, d, sizeof (struct xbsd_disklabel)))
+ fatal (cxt, unable_to_write);
+#endif
+
+ sync_disks ();
+
+ return 1;
+}
+
+static void
+sync_disks (void)
+{
+ printf (_("\nSyncing disks.\n"));
+ sync ();
+ sleep (4);
+}
+
+#if !defined (__alpha__)
+static int
+xbsd_translate_fstype (int linux_type)
+{
+ switch (linux_type)
+ {
+ case 0x01: /* DOS 12-bit FAT */
+ case 0x04: /* DOS 16-bit <32M */
+ case 0x06: /* DOS 16-bit >=32M */
+ case 0xe1: /* DOS access */
+ case 0xe3: /* DOS R/O */
+ case 0xf2: /* DOS secondary */
+ return BSD_FS_MSDOS;
+ case 0x07: /* OS/2 HPFS */
+ return BSD_FS_HPFS;
+ default:
+ return BSD_FS_OTHER;
+ }
+}
+
+static void
+xbsd_link_part (struct fdisk_context *cxt)
+{
+ int k, i;
+ struct partition *p;
+
+ k = get_partition (cxt, 1, partitions);
+
+ if (!xbsd_check_new_partition (&i))
+ return;
+
+ p = get_part_table(k);
+
+ xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(p);
+ xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
+ xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
+}
+#endif
+
+#if defined (__alpha__)
+
+#if !defined(__GLIBC__)
+typedef unsigned long long u_int64_t;
+#endif
+
+void
+alpha_bootblock_checksum (char *boot)
+{
+ u_int64_t *dp, sum;
+ int i;
+
+ dp = (u_int64_t *)boot;
+ sum = 0;
+ for (i = 0; i < 63; i++)
+ sum += dp[i];
+ dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+const struct fdisk_label bsd_label =
+{
+ .name = "bsd",
+ .probe = osf_probe_label,
+ .write = xbsd_write_disklabel,
+ .verify = NULL,
+ .create = xbsd_create_disklabel,
+ .part_add = xbsd_add_part,
+ .part_delete = xbsd_delete_part,
+};
diff --git a/fdisks/fdiskbsdlabel.h b/fdisks/fdiskbsdlabel.h
new file mode 100644
index 0000000..ab6877e
--- /dev/null
+++ b/fdisks/fdiskbsdlabel.h
@@ -0,0 +1,245 @@
+#ifndef FDISK_BSD_LABEL_H
+#define FDISK_BSD_LABEL_H
+
+/*
+ * Copyright (c) 1987, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#ifndef BSD_DISKMAGIC
+#define BSD_DISKMAGIC ((uint32_t) 0x82564557)
+#endif
+
+#ifndef BSD_MAXPARTITIONS
+#define BSD_MAXPARTITIONS 16
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (__i386__) || defined (__sparc__) || defined (__arm__) || \
+ defined (__mips__) || defined (__s390__) || defined (__sh__) || \
+ defined(__x86_64__) || defined (__avr32__) || defined(__cris__)
+#define BSD_LABELSECTOR 1
+#define BSD_LABELOFFSET 0
+#elif defined (__alpha__) || defined (__powerpc__) || defined (__ia64__) || defined (__hppa__)
+#define BSD_LABELSECTOR 0
+#define BSD_LABELOFFSET 64
+#elif defined (__s390__) || defined (__s390x__)
+#define BSD_LABELSECTOR 1
+#define BSD_LABELOFFSET 0
+#else
+#error unknown architecture
+#endif
+
+#define BSD_BBSIZE 8192 /* size of boot area, with label */
+#define BSD_SBSIZE 8192 /* max size of fs superblock */
+
+struct xbsd_disklabel {
+ uint32_t d_magic; /* the magic number */
+ int16_t d_type; /* drive type */
+ int16_t d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+ /* disk geometry: */
+ uint32_t d_secsize; /* # of bytes per sector */
+ uint32_t d_nsectors; /* # of data sectors per track */
+ uint32_t d_ntracks; /* # of tracks per cylinder */
+ uint32_t d_ncylinders; /* # of data cylinders per unit */
+ uint32_t d_secpercyl; /* # of data sectors per cylinder */
+ uint32_t d_secperunit; /* # of data sectors per unit */
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ uint16_t d_sparespertrack; /* # of spare sectors per track */
+ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ uint32_t d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ uint16_t d_rpm; /* rotational speed */
+ uint16_t d_interleave; /* hardware sector interleave */
+ uint16_t d_trackskew; /* sector 0 skew, per track */
+ uint16_t d_cylskew; /* sector 0 skew, per cylinder */
+ uint32_t d_headswitch; /* head switch time, usec */
+ uint32_t d_trkseek; /* track-to-track seek, usec */
+ uint32_t d_flags; /* generic flags */
+#define NDDATA 5
+ uint32_t d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ uint32_t d_spare[NSPARE]; /* reserved for future use */
+ uint32_t d_magic2; /* the magic number (again) */
+ uint16_t d_checksum; /* xor of data incl. partitions */
+ /* filesystem and partition information: */
+ uint16_t d_npartitions; /* number of partitions in following */
+ uint32_t d_bbsize; /* size of boot area at sn0, bytes */
+ uint32_t d_sbsize; /* max size of fs superblock, bytes */
+ struct xbsd_partition { /* the partition table */
+ uint32_t p_size; /* number of sectors in partition */
+ uint32_t p_offset; /* starting sector */
+ uint32_t p_fsize; /* filesystem basic fragment size */
+ uint8_t p_fstype; /* filesystem type, see below */
+ uint8_t p_frag; /* filesystem fragments per block */
+ uint16_t p_cpg; /* filesystem cylinders per group */
+ } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+/* d_type values: */
+#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP 2 /* MSCP */
+#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI 4 /* SCSI */
+#define BSD_DTYPE_ESDI 5 /* ESDI interface */
+#define BSD_DTYPE_ST506 6 /* ST506 etc. */
+#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+#ifdef DKTYPENAMES
+static char *xbsd_dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define BSD_DKMAXTYPES (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1)
+#endif
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED 0 /* unused */
+#define BSD_FS_SWAP 1 /* swap */
+#define BSD_FS_V6 2 /* Sixth Edition */
+#define BSD_FS_V7 3 /* Seventh Edition */
+#define BSD_FS_SYSV 4 /* System V */
+#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS BSD_FS_ISO9660
+#define BSD_FS_BOOT 13 /* partition contains bootstrap */
+#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */
+#define BSD_FS_HFS 15 /* Macintosh HFS */
+#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2 8 /* ext2 file system */
+#else
+#define BSD_FS_MSDOS 8 /* MS-DOS file system */
+#endif
+
+#ifdef DKTYPENAMES
+static struct systypes xbsd_fstypes[] = {
+ {BSD_FS_UNUSED, "unused"},
+ {BSD_FS_SWAP, "swap"},
+ {BSD_FS_V6, "Version 6"},
+ {BSD_FS_V7, "Version 7"},
+ {BSD_FS_SYSV, "System V"},
+ {BSD_FS_V71K, "4.1BSD"},
+ {BSD_FS_V8, "Eighth Edition"},
+ {BSD_FS_BSDFFS, "4.2BSD"},
+#ifdef __alpha__
+ {BSD_FS_EXT2, "ext2"},
+#else
+ {BSD_FS_MSDOS, "MS-DOS"},
+#endif
+ {BSD_FS_BSDLFS, "4.4LFS"},
+ {BSD_FS_OTHER, "unknown"},
+ {BSD_FS_HPFS, "HPFS"},
+ {BSD_FS_ISO9660,"ISO-9660"},
+ {BSD_FS_BOOT, "boot"},
+ {BSD_FS_ADOS, "ADOS"},
+ {BSD_FS_HFS, "HFS"},
+ {BSD_FS_ADVFS, "AdvFS"},
+ { 0, NULL }
+};
+#define BSD_FSMAXTYPES (ARRAY_SIZE(xbsd_fstypes)-1)
+
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01 /* removable media */
+#define BSD_D_ECC 0x02 /* supports ECC */
+#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */
+#define BSD_D_RAMDISK 0x08 /* disk emulator */
+#define BSD_D_CHAIN 0x10 /* can do back-back transfers */
+#define BSD_D_DOSPART 0x20 /* within MSDOS partition */
+
+extern void bsd_command_prompt(struct fdisk_context *cxt);
+extern int btrydev(struct fdisk_context *cxt);
+extern void xbsd_print_disklabel(struct fdisk_context *cxt, int);
+
+#endif /* FDISK_BSD_LABEL_H */
diff --git a/fdisks/fdiskdoslabel.c b/fdisks/fdiskdoslabel.c
new file mode 100644
index 0000000..cc17a03
--- /dev/null
+++ b/fdisks/fdiskdoslabel.c
@@ -0,0 +1,833 @@
+/*
+ * Many, many hands.
+ * Specific DOS label file - Davidlohr Bueso <dave@gnu.org>
+ */
+
+#include <unistd.h>
+#include <ctype.h>
+
+#include "nls.h"
+#include "xalloc.h"
+#include "randutils.h"
+#include "common.h"
+#include "fdisk.h"
+#include "fdiskdoslabel.h"
+
+#define set_hsc(h,s,c,sector) { \
+ s = sector % cxt->geom.sectors + 1; \
+ sector /= cxt->geom.sectors; \
+ h = sector % cxt->geom.heads; \
+ sector /= cxt->geom.heads; \
+ c = sector & 0xff; \
+ s |= (sector >> 2) & 0xc0; \
+ }
+
+#define alignment_required (cxt->grain != cxt->sector_size)
+
+struct pte ptes[MAXIMUM_PARTS];
+sector_t extended_offset;
+int ext_index;
+
+static int get_nonexisting_partition(struct fdisk_context *cxt, int warn, int max)
+{
+ int pno = -1;
+ int i;
+ int dflt = 0;
+
+ for (i = 0; i < max; i++) {
+ struct pte *pe = &ptes[i];
+ struct partition *p = pe->part_table;
+
+ if (p && is_cleared_partition(p)) {
+ if (pno >= 0) {
+ dflt = pno + 1;
+ goto not_unique;
+ }
+ pno = i;
+ }
+ }
+ if (pno >= 0) {
+ printf(_("Selected partition %d\n"), pno+1);
+ return pno;
+ }
+ printf(_("All primary partitions have been defined already!\n"));
+ return -1;
+
+ not_unique:
+ return get_partition_dflt(cxt, warn, max, dflt);
+}
+
+
+/* Allocate a buffer and read a partition table sector */
+static void read_pte(struct fdisk_context *cxt, int pno, sector_t offset)
+{
+ struct pte *pe = &ptes[pno];
+
+ pe->offset = offset;
+ pe->sectorbuffer = xmalloc(cxt->sector_size);
+ read_sector(cxt, offset, pe->sectorbuffer);
+ pe->changed = 0;
+ pe->part_table = pe->ext_pointer = NULL;
+}
+
+static void mbr_set_id(unsigned char *b, unsigned int id)
+{
+ store4_little_endian(&b[440], id);
+}
+
+static void mbr_set_magic(unsigned char *b)
+{
+ b[510] = 0x55;
+ b[511] = 0xaa;
+}
+
+int mbr_is_valid_magic(unsigned char *b)
+{
+ return (b[510] == 0x55 && b[511] == 0xaa);
+}
+
+static unsigned int mbr_get_id(const unsigned char *b)
+{
+ return read4_little_endian(&b[440]);
+}
+
+static void clear_partition(struct partition *p)
+{
+ if (!p)
+ return;
+ p->boot_ind = 0;
+ p->head = 0;
+ p->sector = 0;
+ p->cyl = 0;
+ p->sys_ind = 0;
+ p->end_head = 0;
+ p->end_sector = 0;
+ p->end_cyl = 0;
+ set_start_sect(p,0);
+ set_nr_sects(p,0);
+}
+
+void dos_init(struct fdisk_context *cxt)
+{
+ int i;
+
+ disklabel = DOS_LABEL;
+ partitions = 4;
+ ext_index = 0;
+ extended_offset = 0;
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = &ptes[i];
+
+ pe->part_table = pt_offset(cxt->firstsector, i);
+ pe->ext_pointer = NULL;
+ pe->offset = 0;
+ pe->sectorbuffer = cxt->firstsector;
+ pe->changed = 0;
+ }
+
+ warn_geometry(cxt);
+ warn_limits(cxt);
+ warn_alignment(cxt);
+}
+
+static void dos_delete_partition(
+ struct fdisk_context *cxt __attribute__ ((__unused__)),
+ int partnum)
+{
+ struct pte *pe = &ptes[partnum];
+ struct partition *p = pe->part_table;
+ struct partition *q = pe->ext_pointer;
+
+ /* Note that for the fifth partition (partnum == 4) we don't actually
+ decrement partitions. */
+
+ if (partnum < 4) {
+ if (IS_EXTENDED (p->sys_ind) && partnum == ext_index) {
+ partitions = 4;
+ ptes[ext_index].ext_pointer = NULL;
+ extended_offset = 0;
+ }
+ clear_partition(p);
+ } else if (!q->sys_ind && partnum > 4) {
+ /* the last one in the chain - just delete */
+ --partitions;
+ --partnum;
+ clear_partition(ptes[partnum].ext_pointer);
+ ptes[partnum].changed = 1;
+ } else {
+ /* not the last one - further ones will be moved down */
+ if (partnum > 4) {
+ /* delete this link in the chain */
+ p = ptes[partnum-1].ext_pointer;
+ *p = *q;
+ set_start_sect(p, get_start_sect(q));
+ set_nr_sects(p, get_nr_sects(q));
+ ptes[partnum-1].changed = 1;
+ } else if (partitions > 5) { /* 5 will be moved to 4 */
+ /* the first logical in a longer chain */
+ struct pte *pe = &ptes[5];
+
+ if (pe->part_table) /* prevent SEGFAULT */
+ set_start_sect(pe->part_table,
+ get_partition_start(pe) -
+ extended_offset);
+ pe->offset = extended_offset;
+ pe->changed = 1;
+ }
+
+ if (partitions > 5) {
+ partitions--;
+ while (partnum < partitions) {
+ ptes[partnum] = ptes[partnum+1];
+ partnum++;
+ }
+ } else
+ /* the only logical: clear only */
+ clear_partition(ptes[partnum].part_table);
+ }
+}
+
+static void read_extended(struct fdisk_context *cxt, int ext)
+{
+ int i;
+ struct pte *pex;
+ struct partition *p, *q;
+
+ ext_index = ext;
+ pex = &ptes[ext];
+ pex->ext_pointer = pex->part_table;
+
+ p = pex->part_table;
+ if (!get_start_sect(p)) {
+ fprintf(stderr,
+ _("Bad offset in primary extended partition\n"));
+ return;
+ }
+
+ while (IS_EXTENDED (p->sys_ind)) {
+ struct pte *pe = &ptes[partitions];
+
+ if (partitions >= MAXIMUM_PARTS) {
+ /* This is not a Linux restriction, but
+ this program uses arrays of size MAXIMUM_PARTS.
+ Do not try to `improve' this test. */
+ struct pte *pre = &ptes[partitions-1];
+
+ fprintf(stderr,
+ _("Warning: omitting partitions after #%d.\n"
+ "They will be deleted "
+ "if you save this partition table.\n"),
+ partitions);
+ clear_partition(pre->ext_pointer);
+ pre->changed = 1;
+ return;
+ }
+
+ read_pte(cxt, partitions, extended_offset + get_start_sect(p));
+
+ if (!extended_offset)
+ extended_offset = get_start_sect(p);
+
+ q = p = pt_offset(pe->sectorbuffer, 0);
+ for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
+ if (IS_EXTENDED (p->sys_ind)) {
+ if (pe->ext_pointer)
+ fprintf(stderr,
+ _("Warning: extra link "
+ "pointer in partition table"
+ " %d\n"), partitions + 1);
+ else
+ pe->ext_pointer = p;
+ } else if (p->sys_ind) {
+ if (pe->part_table)
+ fprintf(stderr,
+ _("Warning: ignoring extra "
+ "data in partition table"
+ " %d\n"), partitions + 1);
+ else
+ pe->part_table = p;
+ }
+ }
+
+ /* very strange code here... */
+ if (!pe->part_table) {
+ if (q != pe->ext_pointer)
+ pe->part_table = q;
+ else
+ pe->part_table = q + 1;
+ }
+ if (!pe->ext_pointer) {
+ if (q != pe->part_table)
+ pe->ext_pointer = q;
+ else
+ pe->ext_pointer = q + 1;
+ }
+
+ p = pe->ext_pointer;
+ partitions++;
+ }
+
+ /* remove empty links */
+ remove:
+ for (i = 4; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (!get_nr_sects(pe->part_table) &&
+ (partitions > 5 || ptes[4].part_table->sys_ind)) {
+ printf(_("omitting empty partition (%d)\n"), i+1);
+ dos_delete_partition(cxt, i);
+ goto remove; /* numbering changed */
+ }
+ }
+}
+
+void dos_print_mbr_id(struct fdisk_context *cxt)
+{
+ printf(_("Disk identifier: 0x%08x\n"), mbr_get_id(cxt->firstsector));
+}
+
+static int dos_create_disklabel(struct fdisk_context *cxt)
+{
+ unsigned int id;
+
+ /* random disk signature */
+ random_get_bytes(&id, sizeof(id));
+
+ fprintf(stderr, _("Building a new DOS disklabel with disk identifier 0x%08x.\n"), id);
+
+ dos_init(cxt);
+ fdisk_zeroize_firstsector(cxt);
+ set_all_unchanged();
+ set_changed(0);
+
+ /* Generate an MBR ID for this disk */
+ mbr_set_id(cxt->firstsector, id);
+
+ /* Put MBR signature */
+ mbr_set_magic(cxt->firstsector);
+ return 0;
+}
+
+void dos_set_mbr_id(struct fdisk_context *cxt)
+{
+ unsigned long new_id;
+ char *ep;
+ char ps[64];
+
+ snprintf(ps, sizeof ps, _("New disk identifier (current 0x%08x): "),
+ mbr_get_id(cxt->firstsector));
+
+ if (read_chars(ps) == '\n')
+ return;
+
+ new_id = strtoul(line_ptr, &ep, 0);
+ if (*ep != '\n')
+ return;
+
+ mbr_set_id(cxt->firstsector, new_id);
+ MBRbuffer_changed = 1;
+ dos_print_mbr_id(cxt);
+}
+
+static void get_partition_table_geometry(struct fdisk_context *cxt,
+ unsigned int *ph, unsigned int *ps)
+{
+ unsigned char *bufp = cxt->firstsector;
+ struct partition *p;
+ int i, h, s, hh, ss;
+ int first = 1;
+ int bad = 0;
+
+ hh = ss = 0;
+ for (i=0; i<4; i++) {
+ p = pt_offset(bufp, i);
+ if (p->sys_ind != 0) {
+ h = p->end_head + 1;
+ s = (p->end_sector & 077);
+ if (first) {
+ hh = h;
+ ss = s;
+ first = 0;
+ } else if (hh != h || ss != s)
+ bad = 1;
+ }
+ }
+
+ if (!first && !bad) {
+ *ph = hh;
+ *ps = ss;
+ }
+}
+
+
+static int dos_probe_label(struct fdisk_context *cxt)
+{
+ int i;
+ unsigned int h = 0, s = 0;
+
+ if (!mbr_is_valid_magic(cxt->firstsector))
+ return 0;
+
+ dos_init(cxt);
+
+ get_partition_table_geometry(cxt, &h, &s);
+ if (h && s) {
+ cxt->geom.heads = h;
+ cxt->geom.sectors = s;
+ }
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (IS_EXTENDED (pe->part_table->sys_ind)) {
+ if (partitions != 4)
+ fprintf(stderr, _("Ignoring extra extended "
+ "partition %d\n"), i + 1);
+ else
+ read_extended(cxt, i);
+ }
+ }
+
+ for (i = 3; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (!mbr_is_valid_magic(pe->sectorbuffer)) {
+ fprintf(stderr,
+ _("Warning: invalid flag 0x%04x of partition "
+ "table %d will be corrected by w(rite)\n"),
+ part_table_flag(pe->sectorbuffer), i + 1);
+ pe->changed = 1;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Avoid warning about DOS partitions when no DOS partition was changed.
+ * Here a heuristic "is probably dos partition".
+ * We might also do the opposite and warn in all cases except
+ * for "is probably nondos partition".
+ */
+int is_dos_partition(int t)
+{
+ return (t == 1 || t == 4 || t == 6 ||
+ t == 0x0b || t == 0x0c || t == 0x0e ||
+ t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
+ t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
+ t == 0xc1 || t == 0xc4 || t == 0xc6);
+}
+
+static void set_partition(struct fdisk_context *cxt,
+ int i, int doext, sector_t start,
+ sector_t stop, int sysid)
+{
+ struct partition *p;
+ sector_t offset;
+
+ if (doext) {
+ p = ptes[i].ext_pointer;
+ offset = extended_offset;
+ } else {
+ p = ptes[i].part_table;
+ offset = ptes[i].offset;
+ }
+ p->boot_ind = 0;
+ p->sys_ind = sysid;
+ set_start_sect(p, start - offset);
+ set_nr_sects(p, stop - start + 1);
+
+ if (!doext)
+ print_partition_size(cxt, i + 1, start, stop, sysid);
+
+ if (dos_compatible_flag && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+ start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+ set_hsc(p->head, p->sector, p->cyl, start);
+ if (dos_compatible_flag && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+ stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+ set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
+ ptes[i].changed = 1;
+}
+
+static sector_t get_unused_start(int part_n, sector_t start,
+ sector_t first[], sector_t last[])
+{
+ int i;
+
+ for (i = 0; i < partitions; i++) {
+ sector_t lastplusoff;
+
+ if (start == ptes[i].offset)
+ start += sector_offset;
+ lastplusoff = last[i] + ((part_n < 4) ? 0 : sector_offset);
+ if (start >= first[i] && start <= lastplusoff)
+ start = lastplusoff + 1;
+ }
+
+ return start;
+}
+
+static sector_t align_lba_in_range(struct fdisk_context *cxt,
+ sector_t lba, sector_t start, sector_t stop)
+{
+ start = align_lba(cxt, start, ALIGN_UP);
+ stop = align_lba(cxt, stop, ALIGN_DOWN);
+
+ lba = align_lba(cxt, lba, ALIGN_NEAREST);
+
+ if (lba < start)
+ return start;
+ else if (lba > stop)
+ return stop;
+ return lba;
+}
+
+static void add_partition(struct fdisk_context *cxt, int n, int sys)
+{
+ char mesg[256]; /* 48 does not suffice in Japanese */
+ int i, read = 0;
+ struct partition *p = ptes[n].part_table;
+ struct partition *q = ptes[ext_index].part_table;
+ sector_t start, stop = 0, limit, temp,
+ first[partitions], last[partitions];
+
+ if (p && p->sys_ind) {
+ printf(_("Partition %d is already defined. Delete "
+ "it before re-adding it.\n"), n + 1);
+ return;
+ }
+ fill_bounds(first, last);
+ if (n < 4) {
+ start = sector_offset;
+ if (display_in_cyl_units || !cxt->total_sectors)
+ limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
+ else
+ limit = cxt->total_sectors - 1;
+
+ if (limit > UINT_MAX)
+ limit = UINT_MAX;
+
+ if (extended_offset) {
+ first[ext_index] = extended_offset;
+ last[ext_index] = get_start_sect(q) +
+ get_nr_sects(q) - 1;
+ }
+ } else {
+ start = extended_offset + sector_offset;
+ limit = get_start_sect(q) + get_nr_sects(q) - 1;
+ }
+ if (display_in_cyl_units)
+ for (i = 0; i < partitions; i++)
+ first[i] = (cround(first[i]) - 1) * units_per_sector;
+
+ snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+ do {
+ sector_t dflt, aligned;
+
+ temp = start;
+ dflt = start = get_unused_start(n, start, first, last);
+
+ /* the default sector should be aligned and unused */
+ do {
+ aligned = align_lba_in_range(cxt, dflt, dflt, limit);
+ dflt = get_unused_start(n, aligned, first, last);
+ } while (dflt != aligned && dflt > aligned && dflt < limit);
+
+ if (dflt >= limit)
+ dflt = start;
+ if (start > limit)
+ break;
+ if (start >= temp+units_per_sector && read) {
+ printf(_("Sector %llu is already allocated\n"), temp);
+ temp = start;
+ read = 0;
+ }
+ if (!read && start == temp) {
+ sector_t i = start;
+
+ start = read_int(cxt, cround(i), cround(dflt), cround(limit),
+ 0, mesg);
+ if (display_in_cyl_units) {
+ start = (start - 1) * units_per_sector;
+ if (start < i) start = i;
+ }
+ read = 1;
+ }
+ } while (start != temp || !read);
+ if (n > 4) { /* NOT for fifth partition */
+ struct pte *pe = &ptes[n];
+
+ pe->offset = start - sector_offset;
+ if (pe->offset == extended_offset) { /* must be corrected */
+ pe->offset++;
+ if (sector_offset == 1)
+ start++;
+ }
+ }
+
+ for (i = 0; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (start < pe->offset && limit >= pe->offset)
+ limit = pe->offset - 1;
+ if (start < first[i] && limit >= first[i])
+ limit = first[i] - 1;
+ }
+ if (start > limit) {
+ printf(_("No free sectors available\n"));
+ if (n > 4)
+ partitions--;
+ return;
+ }
+ if (cround(start) == cround(limit)) {
+ stop = limit;
+ } else {
+ int is_suffix_used = 0;
+
+ snprintf(mesg, sizeof(mesg),
+ _("Last %1$s, +%2$s or +size{K,M,G}"),
+ str_units(SINGULAR), str_units(PLURAL));
+
+ stop = read_int_with_suffix(cxt,
+ cround(start), cround(limit), cround(limit),
+ cround(start), mesg, &is_suffix_used);
+ if (display_in_cyl_units) {
+ stop = stop * units_per_sector - 1;
+ if (stop >limit)
+ stop = limit;
+ }
+
+ if (is_suffix_used && alignment_required) {
+ /* the last sector has not been exactly requested (but
+ * defined by +size{K,M,G} convention), so be smart
+ * and align the end of the partition. The next
+ * partition will start at phy.block boundary.
+ */
+ stop = align_lba_in_range(cxt, stop, start, limit) - 1;
+ if (stop > limit)
+ stop = limit;
+ }
+ }
+
+ set_partition(cxt, n, 0, start, stop, sys);
+ if (n > 4)
+ set_partition(cxt, n - 1, 1, ptes[n].offset, stop, EXTENDED);
+
+ if (IS_EXTENDED (sys)) {
+ struct pte *pe4 = &ptes[4];
+ struct pte *pen = &ptes[n];
+
+ ext_index = n;
+ pen->ext_pointer = p;
+ pe4->offset = extended_offset = start;
+ pe4->sectorbuffer = xcalloc(1, cxt->sector_size);
+ pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
+ pe4->ext_pointer = pe4->part_table + 1;
+ pe4->changed = 1;
+ partitions = 5;
+ }
+}
+
+static void add_logical(struct fdisk_context *cxt)
+{
+ if (partitions > 5 || ptes[4].part_table->sys_ind) {
+ struct pte *pe = &ptes[partitions];
+
+ pe->sectorbuffer = xcalloc(1, cxt->sector_size);
+ pe->part_table = pt_offset(pe->sectorbuffer, 0);
+ pe->ext_pointer = pe->part_table + 1;
+ pe->offset = 0;
+ pe->changed = 1;
+ partitions++;
+ }
+ printf(_("Adding logical partition %d\n"), partitions);
+ add_partition(cxt, partitions - 1, LINUX_NATIVE);
+}
+
+static int dos_verify_disklabel(struct fdisk_context *cxt)
+{
+ int i, j;
+ sector_t total = 1, n_sectors = cxt->total_sectors;
+ unsigned long long first[partitions], last[partitions];
+ struct partition *p;
+
+ fill_bounds(first, last);
+ for (i = 0; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ p = pe->part_table;
+ if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) {
+ check_consistency(cxt, p, i);
+ check_alignment(cxt, get_partition_start(pe), i);
+ if (get_partition_start(pe) < first[i])
+ printf(_("Warning: bad start-of-data in "
+ "partition %d\n"), i + 1);
+ check(cxt, i + 1, p->end_head, p->end_sector, p->end_cyl,
+ last[i]);
+ total += last[i] + 1 - first[i];
+ for (j = 0; j < i; j++)
+ if ((first[i] >= first[j] && first[i] <= last[j])
+ || ((last[i] <= last[j] && last[i] >= first[j]))) {
+ printf(_("Warning: partition %d overlaps "
+ "partition %d.\n"), j + 1, i + 1);
+ total += first[i] >= first[j] ?
+ first[i] : first[j];
+ total -= last[i] <= last[j] ?
+ last[i] : last[j];
+ }
+ }
+ }
+
+ if (extended_offset) {
+ struct pte *pex = &ptes[ext_index];
+ sector_t e_last = get_start_sect(pex->part_table) +
+ get_nr_sects(pex->part_table) - 1;
+
+ for (i = 4; i < partitions; i++) {
+ total++;
+ p = ptes[i].part_table;
+ if (!p->sys_ind) {
+ if (i != 4 || i + 1 < partitions)
+ printf(_("Warning: partition %d "
+ "is empty\n"), i + 1);
+ }
+ else if (first[i] < extended_offset ||
+ last[i] > e_last)
+ printf(_("Logical partition %d not entirely in "
+ "partition %d\n"), i + 1, ext_index + 1);
+ }
+ }
+
+ if (total > n_sectors)
+ printf(_("Total allocated sectors %llu greater than the maximum"
+ " %llu\n"), total, n_sectors);
+ else if (total < n_sectors)
+ printf(_("Remaining %lld unallocated %ld-byte sectors\n"),
+ n_sectors - total, cxt->sector_size);
+
+ return 0;
+}
+
+/*
+ * Ask the user for new partition type information (logical, extended).
+ * This function calls the actual partition adding logic - add_partition.
+ *
+ * API callback.
+ */
+static void dos_add_partition(
+ struct fdisk_context *cxt,
+ int partnum __attribute__ ((__unused__)),
+ int parttype)
+{
+ int i, free_primary = 0;
+
+ /* default */
+ parttype = LINUX_NATIVE;
+
+ for (i = 0; i < 4; i++)
+ free_primary += !ptes[i].part_table->sys_ind;
+
+ if (!free_primary && partitions >= MAXIMUM_PARTS) {
+ printf(_("The maximum number of partitions has been created\n"));
+ return;
+ }
+
+ if (!free_primary) {
+ if (extended_offset) {
+ printf(_("All primary partitions are in use\n"));
+ add_logical(cxt);
+ } else
+ printf(_("If you want to create more than four partitions, you must replace a\n"
+ "primary partition with an extended partition first.\n"));
+ } else if (partitions >= MAXIMUM_PARTS) {
+ printf(_("All logical partitions are in use\n"));
+ printf(_("Adding a primary partition\n"));
+ add_partition(cxt, get_partition(cxt, 0, 4), parttype);
+ } else {
+ char c, dflt, line[LINE_LENGTH];
+
+ dflt = (free_primary == 1 && !extended_offset) ? 'e' : 'p';
+ snprintf(line, sizeof(line),
+ _("Partition type:\n"
+ " p primary (%d primary, %d extended, %d free)\n"
+ "%s\n"
+ "Select (default %c): "),
+ 4 - (extended_offset ? 1 : 0) - free_primary, extended_offset ? 1 : 0, free_primary,
+ extended_offset ? _(" l logical (numbered from 5)") : _(" e extended"),
+ dflt);
+
+ c = tolower(read_chars(line));
+ if (c == '\n') {
+ c = dflt;
+ printf(_("Using default response %c\n"), c);
+ }
+ if (c == 'p') {
+ int i = get_nonexisting_partition(cxt, 0, 4);
+ if (i >= 0)
+ add_partition(cxt, i, parttype);
+ return;
+ } else if (c == 'l' && extended_offset) {
+ add_logical(cxt);
+ return;
+ } else if (c == 'e' && !extended_offset) {
+ int i = get_nonexisting_partition(cxt, 0, 4);
+ if (i >= 0)
+ add_partition(cxt, i, EXTENDED);
+ return;
+ } else
+ printf(_("Invalid partition type `%c'\n"), c);
+ }
+}
+
+static int write_sector(struct fdisk_context *cxt, sector_t secno,
+ unsigned char *buf)
+{
+ seek_sector(cxt, secno);
+ if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
+ return -errno;
+ return 0;
+}
+
+static int dos_write_disklabel(struct fdisk_context *cxt)
+{
+ int i, rc = 0;
+
+ /* MBR (primary partitions) */
+ if (!MBRbuffer_changed) {
+ for (i = 0; i < 4; i++)
+ if (ptes[i].changed)
+ MBRbuffer_changed = 1;
+ }
+ if (MBRbuffer_changed) {
+ mbr_set_magic(cxt->firstsector);
+ rc = write_sector(cxt, 0, cxt->firstsector);
+ if (rc)
+ goto done;
+ }
+ /* EBR (logical partitions) */
+ for (i = 4; i < partitions; i++) {
+ struct pte *pe = &ptes[i];
+
+ if (pe->changed) {
+ mbr_set_magic(pe->sectorbuffer);
+ rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
+ if (rc)
+ goto done;
+ }
+ }
+
+done:
+ return rc;
+}
+
+const struct fdisk_label dos_label =
+{
+ .name = "dos",
+ .probe = dos_probe_label,
+ .write = dos_write_disklabel,
+ .verify = dos_verify_disklabel,
+ .create = dos_create_disklabel,
+ .part_add = dos_add_partition,
+ .part_delete = dos_delete_partition,
+};
diff --git a/fdisks/fdiskdoslabel.h b/fdisks/fdiskdoslabel.h
new file mode 100644
index 0000000..0754b17
--- /dev/null
+++ b/fdisks/fdiskdoslabel.h
@@ -0,0 +1,47 @@
+#ifndef FDISK_DOS_LABEL_H
+#define FDISK_DOS_LABEL_H
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer (MBRbuffer)
+ * and have NULL ext_pointer.
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+ struct partition *part_table; /* points into sectorbuffer */
+ struct partition *ext_pointer; /* points into sectorbuffer */
+ char changed; /* boolean */
+ sector_t offset; /* disk sector number */
+ unsigned char *sectorbuffer; /* disk sector contents */
+};
+
+extern struct pte ptes[MAXIMUM_PARTS];
+extern int dos_compatible_flag;
+
+#define pt_offset(b, n) ((struct partition *)((b) + 0x1be + \
+ (n) * sizeof(struct partition)))
+
+extern int ext_index; /* the prime extended partition */
+extern sector_t extended_offset, sector_offset;
+
+/* A valid partition table sector ends in 0x55 0xaa */
+static inline unsigned int part_table_flag(unsigned char *b)
+{
+ return ((unsigned int) b[510]) + (((unsigned int) b[511]) << 8);
+}
+
+static inline sector_t get_partition_start(struct pte *pe)
+{
+ return pe->offset + get_start_sect(pe->part_table);
+}
+
+extern void dos_print_mbr_id(struct fdisk_context *cxt);
+extern void dos_set_mbr_id(struct fdisk_context *cxt);
+extern int is_dos_partition(int t);
+extern void dos_init(struct fdisk_context *cxt);
+
+extern int mbr_is_valid_magic(unsigned char *b);
+
+#endif
diff --git a/fdisks/fdiskmaclabel.c b/fdisks/fdiskmaclabel.c
new file mode 100644
index 0000000..34db9d6
--- /dev/null
+++ b/fdisks/fdiskmaclabel.c
@@ -0,0 +1,108 @@
+/*
+ Changes:
+ Sat Mar 20 09:51:38 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ Internationalization
+*/
+#include <stdio.h> /* stderr */
+#include <string.h> /* strstr */
+#include <unistd.h> /* write */
+
+#include <endian.h>
+
+#include "common.h"
+#include "fdisk.h"
+#include "fdiskmaclabel.h"
+#include "nls.h"
+
+#define MAC_BITMASK 0xffff0000
+
+
+static int other_endian = 0;
+static short volumes=1;
+
+/*
+ * only dealing with free blocks here
+ */
+
+static void
+mac_info( void ) {
+ puts(
+ _("\n\tThere is a valid Mac label on this disk.\n"
+ "\tUnfortunately fdisk(1) cannot handle these disks.\n"
+ "\tUse either pdisk or parted to modify the partition table.\n"
+ "\tNevertheless some advice:\n"
+ "\t1. fdisk will destroy its contents on write.\n"
+ "\t2. Be sure that this disk is NOT a still vital\n"
+ "\t part of a volume group. (Otherwise you may\n"
+ "\t erase the other disks as well, if unmirrored.)\n")
+ );
+}
+
+void
+mac_nolabel(struct fdisk_context *cxt)
+{
+ struct mac_partition *maclabel = (struct mac_partition *) cxt->firstsector;
+
+ maclabel->magic = 0;
+ partitions = 4;
+ fdisk_zeroize_firstsector(cxt);
+ return;
+}
+
+static int
+mac_probe_label(struct fdisk_context *cxt)
+{
+ struct mac_partition *maclabel = (struct mac_partition *) cxt->firstsector;
+
+ /*
+ Conversion: only 16 bit should compared
+ e.g.: HFS Label is only 16bit long
+ */
+ int magic_masked = 0 ;
+ magic_masked = maclabel->magic & MAC_BITMASK ;
+
+ switch (magic_masked) {
+ case MAC_LABEL_MAGIC :
+ case MAC_LABEL_MAGIC_2:
+ case MAC_LABEL_MAGIC_3:
+ goto IS_MAC;
+ break;
+ default:
+ other_endian = 0;
+ return 0;
+
+
+ }
+
+IS_MAC:
+ other_endian = (maclabel->magic == MAC_LABEL_MAGIC_SWAPPED); // =?
+ disklabel = MAC_LABEL;
+ partitions= 1016; // =?
+ volumes = 15; // =?
+ mac_info();
+ mac_nolabel(cxt); /* %% */
+ return 1;
+}
+
+static void mac_add_partition(
+ struct fdisk_context *cxt __attribute__ ((__unused__)),
+ int partnum __attribute__ ((__unused__)),
+ int parttype __attribute__ ((__unused__)))
+{
+ printf(_("\tSorry - this fdisk cannot handle Mac disk labels."
+ "\n\tIf you want to add DOS-type partitions, create"
+ "\n\ta new empty DOS partition table first. (Use o.)"
+ "\n\tWARNING: "
+ "This will destroy the present disk contents.\n"));
+}
+
+const struct fdisk_label mac_label =
+{
+ .name = "mac",
+ .probe = mac_probe_label,
+ .write = NULL,
+ .verify = NULL,
+ .create = NULL,
+ .part_add = mac_add_partition,
+ .part_delete = NULL,
+};
diff --git a/fdisks/fdiskmaclabel.h b/fdisks/fdiskmaclabel.h
new file mode 100644
index 0000000..4aaaadd
--- /dev/null
+++ b/fdisks/fdiskmaclabel.h
@@ -0,0 +1,34 @@
+#ifndef FDISK_MAC_LABEL_H
+#define FDISK_MAC_LABEL_H
+
+#include <sys/types.h>
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be redistributed under
+ * the terms of the GNU Public License.
+ */
+
+struct mac_partition {
+ unsigned int magic; /* expect MAC_LABEL_MAGIC */
+ unsigned int fillbytes1[124];
+ unsigned int physical_volume_id;
+ unsigned int fillbytes2[124];
+};
+
+/* MAC magic number only 16bits, do I always know that there are 0200
+ * following? Problem, after magic the uint16_t res1; follows, I donnno know
+ * about the 200k */
+#define MAC_LABEL_MAGIC 0x45520000
+#define MAC_LABEL_MAGIC_2 0x50530000
+#define MAC_LABEL_MAGIC_3 0x504d0000
+
+#define MAC_LABEL_MAGIC_SWAPPED 0x00002554
+
+#define MAC_LABEL_MAGIC_2_SWAPPED 0x00003505
+#define MAC_LABEL_MAGIC_3_SWAPPED 0x0000d405
+
+/* fdiskmaclabel.c */
+extern struct systypes mac_sys_types[];
+extern void mac_nolabel(struct fdisk_context *cxt);
+
+#endif /* FDISK_MAC_LABEL_H */
diff --git a/fdisks/fdisksgilabel.c b/fdisks/fdisksgilabel.c
new file mode 100644
index 0000000..7253d21
--- /dev/null
+++ b/fdisks/fdisksgilabel.c
@@ -0,0 +1,907 @@
+/*
+ *
+ * fdisksgilabel.c
+ *
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be modified and redistributed under
+ * the terms of the GNU Public License.
+ *
+ * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * Internationalization
+ *
+ * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
+ * Some fixes
+ *
+ * 2012-06-16 Davidlohr Bueso <dave@gnu.org>
+ * Adapt to fdisk context and add heap sort for partitions
+ */
+
+#include <stdio.h> /* stderr */
+#include <stdlib.h> /* exit */
+#include <string.h> /* strstr */
+#include <unistd.h> /* write */
+#include <sys/ioctl.h> /* ioctl */
+#include <sys/stat.h> /* stat */
+#include <assert.h> /* assert */
+
+#include <endian.h>
+#include "nls.h"
+#include "xalloc.h"
+
+#include "blkdev.h"
+
+#include "common.h"
+#include "fdisk.h"
+#include "fdisksgilabel.h"
+#include "fdiskdoslabel.h"
+
+static int other_endian = 0;
+static int debug = 0;
+static short volumes=1;
+
+static sgiinfo *fill_sgiinfo(void);
+
+/*
+ * only dealing with free blocks here
+ */
+
+typedef struct { unsigned int first; unsigned int last; } freeblocks;
+static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */
+
+static void
+setfreelist(int i, unsigned int f, unsigned int l) {
+ if (i < 17) {
+ freelist[i].first = f;
+ freelist[i].last = l;
+ }
+}
+
+static void
+add2freelist(unsigned int f, unsigned int l) {
+ int i = 0;
+ for ( ; i < 17 ; i++)
+ if (freelist[i].last == 0)
+ break;
+ setfreelist(i, f, l);
+}
+
+static void
+clearfreelist(void) {
+ int i;
+
+ for (i = 0; i < 17 ; i++)
+ setfreelist(i, 0, 0);
+}
+
+static unsigned int
+isinfreelist(unsigned int b) {
+ int i;
+
+ for (i = 0; i < 17 ; i++)
+ if (freelist[i].first <= b && freelist[i].last >= b)
+ return freelist[i].last;
+ return 0;
+}
+ /* return last vacant block of this stride (never 0). */
+ /* the '>=' is not quite correct, but simplifies the code */
+/*
+ * end of free blocks section
+ */
+struct systypes sgi_sys_types[] = {
+ {SGI_VOLHDR, N_("SGI volhdr")},
+ {0x01, N_("SGI trkrepl")},
+ {0x02, N_("SGI secrepl")},
+ {SGI_SWAP, N_("SGI raw")},
+ {0x04, N_("SGI bsd")},
+ {0x05, N_("SGI sysv")},
+ {ENTIRE_DISK, N_("SGI volume")},
+ {SGI_EFS, N_("SGI efs")},
+ {0x08, N_("SGI lvol")},
+ {0x09, N_("SGI rlvol")},
+ {SGI_XFS, N_("SGI xfs")},
+ {SGI_XFSLOG, N_("SGI xfslog")},
+ {SGI_XLV, N_("SGI xlv")},
+ {SGI_XVM, N_("SGI xvm")},
+ {LINUX_SWAP, N_("Linux swap")},
+ {LINUX_NATIVE, N_("Linux native")},
+ {LINUX_LVM, N_("Linux LVM")},
+ {LINUX_RAID, N_("Linux RAID")},
+ {0, NULL }
+};
+
+static int
+sgi_get_nsect(struct fdisk_context *cxt) {
+ return SSWAP16(sgilabel->devparam.nsect);
+}
+
+static int
+sgi_get_ntrks(struct fdisk_context *cxt) {
+ return SSWAP16(sgilabel->devparam.ntrks);
+}
+
+static unsigned int
+two_s_complement_32bit_sum(unsigned int *base, int size /* in bytes */) {
+ int i = 0;
+ unsigned int sum = 0;
+
+ size /= sizeof(unsigned int);
+ for (i = 0; i < size; i++)
+ sum -= SSWAP32(base[i]);
+ return sum;
+}
+
+static int
+sgi_probe_label(struct fdisk_context *cxt) {
+ if (sizeof(sgilabel) > 512) {
+ fprintf(stderr,
+ _("According to MIPS Computer Systems, Inc the "
+ "Label must not contain more than 512 bytes\n"));
+ exit(1);
+ }
+
+ if (sgilabel->magic != SGI_LABEL_MAGIC &&
+ sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED) {
+ other_endian = 0;
+ return 0;
+ }
+
+ other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED);
+ /*
+ * test for correct checksum
+ */
+ if (two_s_complement_32bit_sum((unsigned int*)sgilabel,
+ sizeof(*sgilabel))) {
+ fprintf(stderr,
+ _("Detected sgi disklabel with wrong checksum.\n"));
+ }
+ disklabel = SGI_LABEL;
+ partitions= 16;
+ volumes = 15;
+ return 1;
+}
+
+void
+sgi_list_table(struct fdisk_context *cxt, int xtra) {
+ int i, w;
+ int kpi = 0; /* kernel partition ID */
+ char *type;
+
+ w = strlen(cxt->dev_path);
+
+ if (xtra) {
+ printf(_("\nDisk %s (SGI disk label): %d heads, %llu sectors\n"
+ "%llu cylinders, %d physical cylinders\n"
+ "%d extra sects/cyl, interleave %d:1\n"
+ "%s\n"
+ "Units = %s of %d * %ld bytes\n\n"),
+ cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders,
+ SSWAP16(sgiparam.pcylcount),
+ (int) sgiparam.sparecyl, SSWAP16(sgiparam.ilfact),
+ (char *)sgilabel,
+ str_units(PLURAL), units_per_sector,
+ cxt->sector_size);
+ } else {
+ printf(_("\nDisk %s (SGI disk label): "
+ "%d heads, %llu sectors, %llu cylinders\n"
+ "Units = %s of %d * %ld bytes\n\n"),
+ cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders,
+ str_units(PLURAL), units_per_sector,
+ cxt->sector_size);
+ }
+ printf(_("----- partitions -----\n"
+ "Pt# %*s Info Start End Sectors Id System\n"),
+ w + 1, _("Device"));
+ for (i = 0 ; i < partitions; i++) {
+ if (sgi_get_num_sectors(cxt, i) || debug) {
+ uint32_t start = sgi_get_start_sector(cxt, i);
+ uint32_t len = sgi_get_num_sectors(cxt, i);
+ kpi++; /* only count nonempty partitions */
+ printf(
+ "%2d: %s %4s %9ld %9ld %9ld %2x %s\n",
+/* fdisk part number */ i+1,
+/* device */ partname(cxt->dev_path, kpi, w+2),
+/* flags */ (sgi_get_swappartition(cxt) == i) ? "swap" :
+/* flags */ (sgi_get_bootpartition(cxt) == i) ? "boot" : " ",
+/* start */ (long) scround(start),
+/* end */ (long) scround(start+len)-1,
+/* no odd flag on end */ (long) len,
+/* type id */ sgi_get_sysid(cxt, i),
+/* type name */ (type = partition_type(sgi_get_sysid(cxt, i)))
+ ? type : _("Unknown"));
+ }
+ }
+ printf(_("----- Bootinfo -----\nBootfile: %s\n"
+ "----- Directory Entries -----\n"),
+ sgilabel->boot_file);
+ for (i = 0 ; i < volumes; i++) {
+ if (sgilabel->directory[i].vol_file_size) {
+ uint32_t start = SSWAP32(sgilabel->directory[i].vol_file_start);
+ uint32_t len = SSWAP32(sgilabel->directory[i].vol_file_size);
+ unsigned char *name = sgilabel->directory[i].vol_file_name;
+ printf(_("%2d: %-10s sector%5u size%8u\n"),
+ i, name, (unsigned int) start,
+ (unsigned int) len);
+ }
+ }
+}
+
+unsigned int
+sgi_get_start_sector(struct fdisk_context *cxt, int i) {
+ return SSWAP32(sgilabel->partitions[i].start_sector);
+}
+
+unsigned int
+sgi_get_num_sectors(struct fdisk_context *cxt, int i) {
+ return SSWAP32(sgilabel->partitions[i].num_sectors);
+}
+
+int
+sgi_get_sysid(struct fdisk_context *cxt, int i)
+{
+ return SSWAP32(sgilabel->partitions[i].id);
+}
+
+int
+sgi_get_bootpartition(struct fdisk_context *cxt)
+{
+ return (short) SSWAP16(sgilabel->boot_part);
+}
+
+int
+sgi_get_swappartition(struct fdisk_context *cxt)
+{
+ return (short) SSWAP16(sgilabel->swap_part);
+}
+
+void
+sgi_set_bootpartition(struct fdisk_context *cxt, int i)
+{
+ sgilabel->boot_part = SSWAP16(((short)i));
+}
+
+static unsigned int
+sgi_get_lastblock(struct fdisk_context *cxt) {
+ return cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders;
+}
+
+void
+sgi_set_swappartition(struct fdisk_context *cxt, int i) {
+ sgilabel->swap_part = SSWAP16(((short)i));
+}
+
+static int
+sgi_check_bootfile(struct fdisk_context *cxt, const char* aFile) {
+ if (strlen(aFile) < 3) /* "/a\n" is minimum */ {
+ printf(_("\nInvalid Bootfile!\n"
+ "\tThe bootfile must be an absolute non-zero pathname,\n"
+ "\te.g. \"/unix\" or \"/unix.save\".\n"));
+ return 0;
+ } else {
+ if (strlen(aFile) > 16) {
+ printf(_("\n\tName of Bootfile too long: "
+ "16 bytes maximum.\n"));
+ return 0;
+ } else {
+ if (aFile[0] != '/') {
+ printf(_("\n\tBootfile must have a "
+ "fully qualified pathname.\n"));
+ return 0;
+ }
+ }
+ }
+ if (strncmp(aFile, (char *) sgilabel->boot_file, 16)) {
+ printf(_("\n\tBe aware, that the bootfile is not checked for existence.\n\t"
+ "SGI's default is \"/unix\" and for backup \"/unix.save\".\n"));
+ /* filename is correct and did change */
+ return 1;
+ }
+ return 0; /* filename did not change */
+}
+
+void
+sgi_set_bootfile(struct fdisk_context *cxt)
+{
+ printf(_("\nThe current boot file is: %s\n"), sgilabel->boot_file);
+ if (read_chars(_("Please enter the name of the new boot file: ")) == '\n') {
+ printf(_("Boot file unchanged\n"));
+ return;
+ }
+
+ if (sgi_check_bootfile(cxt, line_ptr)) {
+ size_t i = 0;
+ while (i < 16) {
+ if ((line_ptr[i] != '\n') /* in principle caught again by next line */
+ && (strlen(line_ptr) > i))
+ sgilabel->boot_file[i] = line_ptr[i];
+ else
+ sgilabel->boot_file[i] = 0;
+ i++;
+ }
+ printf(_("\n\tBootfile is changed to \"%s\".\n"),
+ sgilabel->boot_file);
+ }
+}
+
+void
+create_sgiinfo(struct fdisk_context *cxt) {
+ /* I keep SGI's habit to write the sgilabel to the second block */
+ sgilabel->directory[0].vol_file_start = SSWAP32(2);
+ sgilabel->directory[0].vol_file_size = SSWAP32(sizeof(sgiinfo));
+ strncpy((char *) sgilabel->directory[0].vol_file_name, "sgilabel", 8);
+}
+
+
+static int sgi_write_disklabel(struct fdisk_context *cxt)
+{
+ sgiinfo *info = NULL;
+
+ sgilabel->csum = 0;
+ sgilabel->csum = SSWAP32(two_s_complement_32bit_sum(
+ (unsigned int*)sgilabel,
+ sizeof(*sgilabel)));
+ assert(two_s_complement_32bit_sum(
+ (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
+ if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+ goto err;
+ if (write(cxt->dev_fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE)
+ goto err;
+ if (!strncmp((char *) sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
+ /*
+ * keep this habit of first writing the "sgilabel".
+ * I never tested whether it works without (AN 981002).
+ */
+ int infostartblock
+ = SSWAP32(sgilabel->directory[0].vol_file_start);
+
+ if (lseek(cxt->dev_fd, (off_t) infostartblock *
+ SECTOR_SIZE, SEEK_SET) < 0)
+ goto err;
+
+ info = fill_sgiinfo();
+ if (!info)
+ goto err;
+
+ if (write(cxt->dev_fd, info, SECTOR_SIZE) != SECTOR_SIZE)
+ goto err;
+ }
+
+ free(info);
+ return 0;
+err:
+ free(info);
+ return -errno;
+}
+
+static int
+compare_start(struct fdisk_context *cxt, const void *x, const void *y) {
+ /*
+ * sort according to start sectors
+ * and prefers largest partition:
+ * entry zero is entire disk entry
+ */
+ unsigned int i = *(int *) x;
+ unsigned int j = *(int *) y;
+ unsigned int a = sgi_get_start_sector(cxt, i);
+ unsigned int b = sgi_get_start_sector(cxt, j);
+ unsigned int c = sgi_get_num_sectors(cxt, i);
+ unsigned int d = sgi_get_num_sectors(cxt, j);
+
+ if (a == b)
+ return (d > c) ? 1 : (d == c) ? 0 : -1;
+ return (a > b) ? 1 : -1;
+}
+
+static void generic_swap(void *a, void *b, int size)
+{
+ char t;
+
+ do {
+ t = *(char *)a;
+ *(char *)a++ = *(char *)b;
+ *(char *)b++ = t;
+ } while (--size > 0);
+}
+
+
+/* heap sort, based on Matt Mackall's linux kernel version */
+static void sort(void *base, size_t num, size_t size, struct fdisk_context *cxt,
+ int (*cmp_func)(struct fdisk_context *, const void *, const void *))
+{
+ /* pre-scale counters for performance */
+ int i = (num/2 - 1) * size;
+ size_t n = num * size, c, r;
+
+ /* heapify */
+ for ( ; i >= 0; i -= size) {
+ for (r = i; r * 2 + size < n; r = c) {
+ c = r * 2 + size;
+ if (c < n - size &&
+ cmp_func(cxt, base + c, base + c + size) < 0)
+ c += size;
+ if (cmp_func(cxt, base + r, base + c) >= 0)
+ break;
+ generic_swap(base + r, base + c, size);
+ }
+ }
+
+ /* sort */
+ for (i = n - size; i > 0; i -= size) {
+ generic_swap(base, base + i, size);
+ for (r = 0; r * 2 + size < (size_t) i; r = c) {
+ c = r * 2 + size;
+ if (c < i - size &&
+ cmp_func(cxt, base + c, base + c + size) < 0)
+ c += size;
+ if (cmp_func(cxt, base + r, base + c) >= 0)
+ break;
+ generic_swap(base + r, base + c, size);
+ }
+ }
+}
+
+static int sgi_verify_disklabel(struct fdisk_context *cxt)
+{
+ int Index[16]; /* list of valid partitions */
+ int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */
+ int entire = 0, i = 0, verbose = 1;
+ unsigned int start = 0;
+ long long gap = 0; /* count unused blocks */
+ unsigned int lastblock = sgi_get_lastblock(cxt);
+
+ clearfreelist();
+ for (i=0; i<16; i++) {
+ if (sgi_get_num_sectors(cxt, i) != 0) {
+ Index[sortcount++]=i;
+ if (sgi_get_sysid(cxt, i) == ENTIRE_DISK) {
+ if (entire++ == 1) {
+ if (verbose)
+ printf(_("More than one entire disk entry present.\n"));
+ }
+ }
+ }
+ }
+ if (sortcount == 0) {
+ if (verbose)
+ printf(_("No partitions defined\n"));
+ return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+ }
+
+ sort(Index, sortcount, sizeof(Index[0]), cxt, compare_start);
+
+ if (sgi_get_sysid(cxt, Index[0]) == ENTIRE_DISK) {
+ if ((Index[0] != 10) && verbose)
+ printf(_("IRIX likes when Partition 11 covers the entire disk.\n"));
+ if ((sgi_get_start_sector(cxt, Index[0]) != 0) && verbose)
+ printf(_("The entire disk partition should start "
+ "at block 0,\n"
+ "not at diskblock %d.\n"),
+ sgi_get_start_sector(cxt, Index[0]));
+ if (debug) /* I do not understand how some disks fulfil it */
+ if ((sgi_get_num_sectors(cxt, Index[0]) != lastblock) && verbose)
+ printf(_("The entire disk partition is only %d diskblock large,\n"
+ "but the disk is %d diskblocks long.\n"),
+ sgi_get_num_sectors(cxt, Index[0]), lastblock);
+ lastblock = sgi_get_num_sectors(cxt, Index[0]);
+ } else {
+ if (verbose)
+ printf(_("Partition 11 should cover the entire disk.\n"));
+ if (debug>2)
+ printf("sysid=%d\tpartition=%d\n",
+ sgi_get_sysid(cxt, Index[0]), Index[0]+1);
+ }
+ for (i=1, start=0; i<sortcount; i++) {
+ int cylsize = sgi_get_nsect(cxt) * sgi_get_ntrks(cxt);
+ if ((sgi_get_start_sector(cxt, Index[i]) % cylsize) != 0) {
+ if (debug) /* I do not understand how some disks fulfil it */
+ if (verbose)
+ printf(_("Partition %d does not start on cylinder boundary.\n"),
+ Index[i]+1);
+ }
+ if (sgi_get_num_sectors(cxt, Index[i]) % cylsize != 0) {
+ if (debug) /* I do not understand how some disks fulfil it */
+ if (verbose)
+ printf(_("Partition %d does not end on cylinder boundary.\n"),
+ Index[i]+1);
+ }
+ /* We cannot handle several "entire disk" entries. */
+ if (sgi_get_sysid(cxt, Index[i]) == ENTIRE_DISK) continue;
+ if (start > sgi_get_start_sector(cxt, Index[i])) {
+ if (verbose)
+ printf(_("The Partition %d and %d overlap by %d sectors.\n"),
+ Index[i-1]+1, Index[i]+1,
+ start - sgi_get_start_sector(cxt, Index[i]));
+ if (gap > 0) gap = -gap;
+ if (gap == 0) gap = -1;
+ }
+ if (start < sgi_get_start_sector(cxt, Index[i])) {
+ if (verbose)
+ printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"),
+ sgi_get_start_sector(cxt, Index[i]) - start,
+ start, sgi_get_start_sector(cxt, Index[i])-1);
+ gap += sgi_get_start_sector(cxt, Index[i]) - start;
+ add2freelist(start, sgi_get_start_sector(cxt, Index[i]));
+ }
+ start = sgi_get_start_sector(cxt, Index[i])
+ + sgi_get_num_sectors(cxt, Index[i]);
+ /* Align free space on cylinder boundary */
+ if (start % cylsize)
+ start += cylsize - (start % cylsize);
+ if (debug > 1) {
+ if (verbose)
+ printf("%2d:%12d\t%12d\t%12d\n", Index[i],
+ sgi_get_start_sector(cxt, Index[i]),
+ sgi_get_num_sectors(cxt, Index[i]),
+ sgi_get_sysid(cxt, Index[i]));
+ }
+ }
+ if (start < lastblock) {
+ if (verbose)
+ printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"),
+ lastblock - start, start, lastblock-1);
+ gap += lastblock - start;
+ add2freelist(start, lastblock);
+ }
+ /*
+ * Done with arithmetics
+ * Go for details now
+ */
+ if (verbose) {
+ if (sgi_get_bootpartition(cxt) < 0 || !sgi_get_num_sectors(cxt, sgi_get_bootpartition(cxt))) {
+ printf(_("\nThe boot partition does not exist.\n"));
+ }
+ if (sgi_get_swappartition(cxt) < 0 || !sgi_get_num_sectors(cxt, sgi_get_swappartition(cxt))) {
+ printf(_("\nThe swap partition does not exist.\n"));
+ } else {
+ if ((sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != SGI_SWAP)
+ && (sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != LINUX_SWAP))
+ printf(_("\nThe swap partition has no swap type.\n"));
+ }
+ if (sgi_check_bootfile(cxt, "/unix"))
+ printf(_("\tYou have chosen an unusual boot file name.\n"));
+ }
+ return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int
+sgi_gaps(struct fdisk_context *cxt) {
+ /*
+ * returned value is:
+ * = 0 : disk is properly filled to the rim
+ * < 0 : there is an overlap
+ * > 0 : there is still some vacant space
+ */
+ return sgi_verify_disklabel(cxt);
+}
+
+int
+sgi_change_sysid(struct fdisk_context *cxt, int i, int sys)
+{
+ if (sgi_get_num_sectors(cxt, i) == 0) /* caught already before, ... */ {
+ printf(_("Sorry, only for non-empty partitions you can change the tag.\n"));
+ return 0;
+ }
+ if (((sys != ENTIRE_DISK) && (sys != SGI_VOLHDR))
+ && (sgi_get_start_sector(cxt, i)<1)) {
+ read_chars(
+ _("It is highly recommended that the partition at offset 0\n"
+ "is of type \"SGI volhdr\", the IRIX system will rely on it to\n"
+ "retrieve from its directory standalone tools like sash and fx.\n"
+ "Only the \"SGI volume\" entire disk section may violate this.\n"
+ "Type YES if you are sure about tagging this partition differently.\n"));
+ if (strcmp (line_ptr, _("YES\n")))
+ return 0;
+ }
+ sgilabel->partitions[i].id = SSWAP32(sys);
+ return 1;
+}
+
+/* returns partition index of first entry marked as entire disk */
+static int
+sgi_entire(struct fdisk_context *cxt) {
+ int i;
+
+ for (i=0; i<16; i++)
+ if (sgi_get_sysid(cxt, i) == SGI_VOLUME)
+ return i;
+ return -1;
+}
+
+static void
+sgi_set_partition(struct fdisk_context *cxt,
+ int i, unsigned int start, unsigned int length, int sys) {
+ sgilabel->partitions[i].id = SSWAP32(sys);
+ sgilabel->partitions[i].num_sectors = SSWAP32(length);
+ sgilabel->partitions[i].start_sector = SSWAP32(start);
+ set_changed(i);
+ if (sgi_gaps(cxt) < 0) /* rebuild freelist */
+ printf(_("Partition overlap on the disk.\n"));
+ if (length)
+ print_partition_size(cxt, i + 1, start, start + length, sys);
+}
+
+static void
+sgi_set_entire(struct fdisk_context *cxt) {
+ int n;
+
+ for (n=10; n<partitions; n++) {
+ if (!sgi_get_num_sectors(cxt, n)) {
+ sgi_set_partition(cxt, n, 0, sgi_get_lastblock(cxt), SGI_VOLUME);
+ break;
+ }
+ }
+}
+
+static
+void
+sgi_set_volhdr(struct fdisk_context *cxt)
+{
+ int n;
+
+ for (n=8; n<partitions; n++) {
+ if (!sgi_get_num_sectors(cxt, n)) {
+ /*
+ * Choose same default volume header size
+ * as IRIX fx uses.
+ */
+ if (4096 < sgi_get_lastblock(cxt))
+ sgi_set_partition(cxt, n, 0, 4096, SGI_VOLHDR);
+ break;
+ }
+ }
+}
+
+static void sgi_delete_partition(struct fdisk_context *cxt, int partnum)
+{
+ sgi_set_partition(cxt, partnum, 0, 0, 0);
+}
+
+static void sgi_add_partition(struct fdisk_context *cxt, int n, int sys)
+{
+ char mesg[256];
+ unsigned int first=0, last=0;
+
+ if (n == 10) {
+ sys = SGI_VOLUME;
+ } else if (n == 8) {
+ sys = 0;
+ }
+ if (sgi_get_num_sectors(cxt, n)) {
+ printf(_("Partition %d is already defined. Delete "
+ "it before re-adding it.\n"), n + 1);
+ return;
+ }
+ if ((sgi_entire(cxt) == -1)
+ && (sys != SGI_VOLUME)) {
+ printf(_("Attempting to generate entire disk entry automatically.\n"));
+ sgi_set_entire(cxt);
+ sgi_set_volhdr(cxt);
+ }
+ if ((sgi_gaps(cxt) == 0) && (sys != SGI_VOLUME)) {
+ printf(_("The entire disk is already covered with partitions.\n"));
+ return;
+ }
+ if (sgi_gaps(cxt) < 0) {
+ printf(_("You got a partition overlap on the disk. Fix it first!\n"));
+ return;
+ }
+ snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+ for (;;) {
+ if (sys == SGI_VOLUME) {
+ last = sgi_get_lastblock(cxt);
+ first = read_int(cxt, 0, 0, last-1, 0, mesg);
+ if (first != 0) {
+ printf(_("It is highly recommended that eleventh partition\n"
+ "covers the entire disk and is of type `SGI volume'\n"));
+ }
+ } else {
+ first = freelist[0].first;
+ last = freelist[0].last;
+ first = read_int(cxt, scround(first), scround(first), scround(last)-1,
+ 0, mesg);
+ }
+ if (display_in_cyl_units)
+ first *= units_per_sector;
+ /*else
+ first = first; * align to cylinder if you know how ... */
+ if (!last)
+ last = isinfreelist(first);
+ if (last == 0) {
+ printf(_("You will get a partition overlap on the disk. "
+ "Fix it first!\n"));
+ } else
+ break;
+ }
+ snprintf(mesg, sizeof(mesg), _(" Last %s"), str_units(SINGULAR));
+ last = read_int(cxt, scround(first), scround(last)-1, scround(last)-1,
+ scround(first), mesg)+1;
+ if (display_in_cyl_units)
+ last *= units_per_sector;
+ /*else
+ last = last; * align to cylinder if You know how ... */
+ if ((sys == SGI_VOLUME) && (first != 0 || last != sgi_get_lastblock(cxt)))
+ printf(_("It is highly recommended that eleventh partition\n"
+ "covers the entire disk and is of type `SGI volume'\n"));
+ sgi_set_partition(cxt, n, first, last-first, sys);
+}
+
+static int sgi_create_disklabel(struct fdisk_context *cxt)
+{
+ struct hd_geometry geometry;
+ struct {
+ unsigned int start;
+ unsigned int nsect;
+ int sysid;
+ } old[4];
+ int i=0;
+ sector_t llsectors;
+ int res; /* the result from the ioctl */
+ int sec_fac; /* the sector factor */
+
+ sec_fac = cxt->sector_size / 512; /* determine the sector factor */
+
+ fprintf(stderr,
+ _("Building a new SGI disklabel.\n"));
+
+ other_endian = (BYTE_ORDER == LITTLE_ENDIAN);
+
+ res = blkdev_get_sectors(cxt->dev_fd, &llsectors);
+
+#ifdef HDIO_GETGEO
+ if (ioctl(cxt->dev_fd, HDIO_GETGEO, &geometry) < 0)
+ err(EXIT_FAILURE, _("HDIO_GETGEO ioctl failed on %s"), cxt->dev_path);
+
+ cxt->geom.heads = geometry.heads;
+ cxt->geom.sectors = geometry.sectors;
+ if (res == 0) {
+ /* the get device size ioctl was successful */
+ sector_t llcyls;
+ llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+ cxt->geom.cylinders = llcyls;
+ if (cxt->geom.cylinders != llcyls) /* truncated? */
+ cxt->geom.cylinders = ~0;
+ } else {
+ /* otherwise print error and use truncated version */
+ cxt->geom.cylinders = geometry.cylinders;
+ fprintf(stderr,
+ _("Warning: BLKGETSIZE ioctl failed on %s. "
+ "Using geometry cylinder value of %llu.\n"
+ "This value may be truncated for devices"
+ " > 33.8 GB.\n"), cxt->dev_path, cxt->geom.cylinders);
+ }
+#endif
+ /*
+ * Convert old MBR to SGI label, make it DEPRECATED, this feature
+ * has to be handled in by any top-level fdisk command.
+ */
+ for (i = 0; i < 4; i++) {
+ old[i].sysid = 0;
+ if (mbr_is_valid_magic(cxt->firstsector)) {
+ if (get_part_table(i)->sys_ind) {
+ old[i].sysid = get_part_table(i)->sys_ind;
+ old[i].start = get_start_sect(get_part_table(i));
+ old[i].nsect = get_nr_sects(get_part_table(i));
+ if (debug)
+ printf(_("ID=%02x\tSTART=%d\tLENGTH=%d\n"),
+ old[i].sysid, old[i].start, old[i].nsect);
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ if (old[i].sysid) {
+ printf(_("Trying to keep parameters of partitions already set.\n"));
+ break;
+ }
+
+ fdisk_zeroize_firstsector(cxt);
+ sgilabel->magic = SSWAP32(SGI_LABEL_MAGIC);
+ sgilabel->boot_part = SSWAP16(0);
+ sgilabel->swap_part = SSWAP16(1);
+
+ /* sizeof(sgilabel->boot_file) = 16 > 6 */
+ memset(sgilabel->boot_file, 0, 16);
+ strcpy((char *) sgilabel->boot_file, "/unix");
+
+ sgilabel->devparam.skew = (0);
+ sgilabel->devparam.gap1 = (0);
+ sgilabel->devparam.gap2 = (0);
+ sgilabel->devparam.sparecyl = (0);
+ sgilabel->devparam.pcylcount = SSWAP16(geometry.cylinders);
+ sgilabel->devparam.head_vol0 = SSWAP16(0);
+ sgilabel->devparam.ntrks = SSWAP16(geometry.heads);
+ /* tracks/cylinder (heads) */
+ sgilabel->devparam.cmd_tag_queue_depth = (0);
+ sgilabel->devparam.unused0 = (0);
+ sgilabel->devparam.unused1 = SSWAP16(0);
+ sgilabel->devparam.nsect = SSWAP16(geometry.sectors);
+ /* sectors/track */
+ sgilabel->devparam.bytes = SSWAP16(cxt->sector_size);
+ sgilabel->devparam.ilfact = SSWAP16(1);
+ sgilabel->devparam.flags = SSWAP32(TRACK_FWD|\
+ IGNORE_ERRORS|RESEEK);
+ sgilabel->devparam.datarate = SSWAP32(0);
+ sgilabel->devparam.retries_on_error = SSWAP32(1);
+ sgilabel->devparam.ms_per_word = SSWAP32(0);
+ sgilabel->devparam.xylogics_gap1 = SSWAP16(0);
+ sgilabel->devparam.xylogics_syncdelay = SSWAP16(0);
+ sgilabel->devparam.xylogics_readdelay = SSWAP16(0);
+ sgilabel->devparam.xylogics_gap2 = SSWAP16(0);
+ sgilabel->devparam.xylogics_readgate = SSWAP16(0);
+ sgilabel->devparam.xylogics_writecont = SSWAP16(0);
+ memset(&(sgilabel->directory), 0, sizeof(struct volume_directory)*15);
+ memset(&(sgilabel->partitions), 0, sizeof(struct sgi_partition)*16);
+ disklabel = SGI_LABEL;
+ partitions = 16;
+ volumes = 15;
+ sgi_set_entire(cxt);
+ sgi_set_volhdr(cxt);
+ for (i = 0; i < 4; i++) {
+ if (old[i].sysid) {
+ sgi_set_partition(cxt, i, old[i].start, old[i].nsect, old[i].sysid);
+ }
+ }
+ return 0;
+}
+
+void
+sgi_set_ilfact(void)
+{
+ /* do nothing in the beginning */
+}
+
+void
+sgi_set_rspeed(void)
+{
+ /* do nothing in the beginning */
+}
+
+void
+sgi_set_pcylcount(void)
+{
+ /* do nothing in the beginning */
+}
+
+void
+sgi_set_xcyl(void)
+{
+ /* do nothing in the beginning */
+}
+
+void
+sgi_set_ncyl(void)
+{
+ /* do nothing in the beginning */
+}
+
+/* _____________________________________________________________
+ */
+
+static sgiinfo *fill_sgiinfo(void)
+{
+ sgiinfo *info = xcalloc(1, sizeof(sgiinfo));
+
+ if (!info)
+ return NULL;
+
+ info->magic=SSWAP32(SGI_INFO_MAGIC);
+ info->b1=SSWAP32(-1);
+ info->b2=SSWAP16(-1);
+ info->b3=SSWAP16(1);
+ /* You may want to replace this string !!!!!!! */
+ strcpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30");
+ strcpy((char *) info->serial, "0000");
+ info->check1816 = SSWAP16(18*256 +16);
+ strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994");
+ return info;
+}
+
+const struct fdisk_label sgi_label =
+{
+ .name = "sgi",
+ .probe = sgi_probe_label,
+ .write = sgi_write_disklabel,
+ .verify = sgi_verify_disklabel,
+ .create = sgi_create_disklabel,
+ .part_add = sgi_add_partition,
+ .part_delete = sgi_delete_partition,
+};
diff --git a/fdisks/fdisksgilabel.h b/fdisks/fdisksgilabel.h
new file mode 100644
index 0000000..4d51113
--- /dev/null
+++ b/fdisks/fdisksgilabel.h
@@ -0,0 +1,131 @@
+#ifndef FDISK_SGI_LABEL_H
+#define FDISK_SGI_LABEL_H
+
+#include <stdint.h>
+
+#include "bitops.h"
+
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ * This file may be modified and redistributed under
+ * the terms of the GNU Public License.
+ */
+
+struct device_parameter { /* 48 bytes */
+ unsigned char skew;
+ unsigned char gap1;
+ unsigned char gap2;
+ unsigned char sparecyl;
+ unsigned short pcylcount;
+ unsigned short head_vol0;
+ unsigned short ntrks; /* tracks in cyl 0 or vol 0 */
+ unsigned char cmd_tag_queue_depth;
+ unsigned char unused0;
+ unsigned short unused1;
+ unsigned short nsect; /* sectors/tracks in cyl 0 or vol 0 */
+ unsigned short bytes;
+ unsigned short ilfact;
+ unsigned int flags; /* controller flags */
+ unsigned int datarate;
+ unsigned int retries_on_error;
+ unsigned int ms_per_word;
+ unsigned short xylogics_gap1;
+ unsigned short xylogics_syncdelay;
+ unsigned short xylogics_readdelay;
+ unsigned short xylogics_gap2;
+ unsigned short xylogics_readgate;
+ unsigned short xylogics_writecont;
+};
+
+#define SGI_VOLHDR 0x00
+/* 1 and 2 were used for drive types no longer supported by SGI */
+#define SGI_SWAP 0x03
+/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */
+#define SGI_VOLUME 0x06
+#define SGI_EFS 0x07
+#define SGI_LVOL 0x08
+#define SGI_RLVOL 0x09
+#define SGI_XFS 0x0a
+#define SGI_XFSLOG 0x0b
+#define SGI_XLV 0x0c
+#define SGI_XVM 0x0d
+#define ENTIRE_DISK SGI_VOLUME
+/*
+ * controller flags
+ */
+#define SECTOR_SLIP 0x01
+#define SECTOR_FWD 0x02
+#define TRACK_FWD 0x04
+#define TRACK_MULTIVOL 0x08
+#define IGNORE_ERRORS 0x10
+#define RESEEK 0x20
+#define CMDTAGQ_ENABLE 0x40
+
+typedef struct {
+ unsigned int magic; /* expect SGI_LABEL_MAGIC */
+ short boot_part; /* active boot partition */
+ short swap_part; /* active swap partition */
+ unsigned char boot_file[16]; /* name of the bootfile */
+ struct device_parameter devparam; /* 1 * 48 bytes */
+ struct volume_directory { /* 15 * 16 bytes */
+ unsigned char vol_file_name[8]; /* a character array */
+ unsigned int vol_file_start; /* number of logical block */
+ unsigned int vol_file_size; /* number of bytes */
+ } directory[15];
+ struct sgi_partition { /* 16 * 12 bytes */
+ unsigned int num_sectors; /* number of blocks */
+ unsigned int start_sector; /* must be cylinder aligned */
+ unsigned int id;
+ } partitions[16];
+ unsigned int csum;
+ unsigned int fillbytes;
+} sgi_partition;
+
+typedef struct {
+ unsigned int magic; /* looks like a magic number */
+ unsigned int a2;
+ unsigned int a3;
+ unsigned int a4;
+ unsigned int b1;
+ unsigned short b2;
+ unsigned short b3;
+ unsigned int c[16];
+ unsigned short d[3];
+ unsigned char scsi_string[50];
+ unsigned char serial[137];
+ unsigned short check1816;
+ unsigned char installer[225];
+} sgiinfo;
+
+#define SGI_LABEL_MAGIC 0x0be5a941
+#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b
+#define SGI_INFO_MAGIC 0x00072959
+#define SGI_INFO_MAGIC_SWAPPED 0x59290700
+
+#define SSWAP16(x) (other_endian ? swab16(x) : (uint16_t)(x))
+#define SSWAP32(x) (other_endian ? swab32(x) : (uint32_t)(x))
+
+/* fdisk.c */
+#define sgilabel ((sgi_partition *)cxt->firstsector)
+#define sgiparam (sgilabel->devparam)
+
+/* fdisksgilabel.c */
+extern struct systypes sgi_sys_types[];
+extern void sgi_list_table( struct fdisk_context *cxt, int xtra );
+extern int sgi_change_sysid(struct fdisk_context *cxt, int i, int sys);
+extern unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i );
+extern unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i );
+extern int sgi_get_sysid(struct fdisk_context *cxt, int i );
+extern void create_sgiinfo(struct fdisk_context *cxt);
+extern void sgi_set_ilfact( void );
+extern void sgi_set_rspeed( void );
+extern void sgi_set_pcylcount( void );
+extern void sgi_set_xcyl( void );
+extern void sgi_set_ncyl( void );
+extern void sgi_set_bootpartition(struct fdisk_context *cxt, int i );
+extern void sgi_set_swappartition(struct fdisk_context *cxt, int i );
+extern int sgi_get_bootpartition(struct fdisk_context *cxt);
+extern int sgi_get_swappartition(struct fdisk_context *cxt);
+extern void sgi_set_bootfile(struct fdisk_context *cxt);
+
+#endif /* FDISK_SGI_LABEL_H */
diff --git a/fdisks/fdisksunlabel.c b/fdisks/fdisksunlabel.c
new file mode 100644
index 0000000..9d17ed2
--- /dev/null
+++ b/fdisks/fdisksunlabel.c
@@ -0,0 +1,661 @@
+/*
+ * fdisksunlabel.c
+ *
+ * I think this is mostly, or entirely, due to
+ * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *
+ * Merged with fdisk for other architectures, aeb, June 1998.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * Internationalization
+ */
+
+#include <stdio.h> /* stderr */
+#include <stdlib.h> /* qsort */
+#include <string.h> /* strstr */
+#include <unistd.h> /* write */
+#include <sys/ioctl.h> /* ioctl */
+
+#include "nls.h"
+#include "blkdev.h"
+
+#include <endian.h>
+
+#include "common.h"
+#include "fdisk.h"
+#include "fdisksunlabel.h"
+
+static int other_endian = 0;
+
+struct systypes sun_sys_types[] = {
+ {SUN_TAG_UNASSIGNED, N_("Unassigned")},
+ {SUN_TAG_BOOT, N_("Boot")},
+ {SUN_TAG_ROOT, N_("SunOS root")},
+ {SUN_TAG_SWAP, N_("SunOS swap")},
+ {SUN_TAG_USR, N_("SunOS usr")},
+ {SUN_TAG_BACKUP, N_("Whole disk")},
+ {SUN_TAG_STAND, N_("SunOS stand")},
+ {SUN_TAG_VAR, N_("SunOS var")},
+ {SUN_TAG_HOME, N_("SunOS home")},
+ {SUN_TAG_ALTSCTR, N_("SunOS alt sectors")},
+ {SUN_TAG_CACHE, N_("SunOS cachefs")},
+ {SUN_TAG_RESERVED, N_("SunOS reserved")},
+ {SUN_TAG_LINUX_SWAP, N_("Linux swap")},
+ {SUN_TAG_LINUX_NATIVE, N_("Linux native")},
+ {SUN_TAG_LINUX_LVM, N_("Linux LVM")},
+ {SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")},
+ { 0, NULL }
+};
+
+static inline unsigned short __swap16(unsigned short x) {
+ return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8);
+}
+static inline uint32_t __swap32(uint32_t x) {
+ return (((uint32_t)(x) & 0xFF) << 24) | (((uint32_t)(x) & 0xFF00) << 8) | (((uint32_t)(x) & 0xFF0000) >> 8) | (((uint32_t)(x) & 0xFF000000) >> 24);
+}
+
+#define SSWAP16(x) (other_endian ? __swap16(x) \
+ : (uint16_t)(x))
+#define SSWAP32(x) (other_endian ? __swap32(x) \
+ : (uint32_t)(x))
+
+static void set_sun_partition(struct fdisk_context *cxt,
+ int i, uint32_t start, uint32_t stop, uint16_t sysid)
+{
+ sunlabel->part_tags[i].tag = SSWAP16(sysid);
+ sunlabel->part_tags[i].flag = SSWAP16(0);
+ sunlabel->partitions[i].start_cylinder =
+ SSWAP32(start / (cxt->geom.heads * cxt->geom.sectors));
+ sunlabel->partitions[i].num_sectors =
+ SSWAP32(stop - start);
+ set_changed(i);
+ print_partition_size(cxt, i + 1, start, stop, sysid);
+}
+
+static void init(void)
+{
+ disklabel = SUN_LABEL;
+ partitions = SUN_NUM_PARTITIONS;
+}
+
+static int sun_probe_label(struct fdisk_context *cxt)
+{
+ unsigned short *ush;
+ int csum;
+
+ if (sunlabel->magic != SUN_LABEL_MAGIC &&
+ sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
+ other_endian = 0;
+ return 0;
+ }
+
+ init();
+ other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
+
+ ush = ((unsigned short *) (sunlabel + 1)) - 1;
+ for (csum = 0; ush >= (unsigned short *)sunlabel;)
+ csum ^= *ush--;
+
+ if (csum) {
+ fprintf(stderr,_("Detected sun disklabel with wrong checksum.\n"
+ "Probably you'll have to set all the values,\n"
+ "e.g. heads, sectors, cylinders and partitions\n"
+ "or force a fresh label (s command in main menu)\n"));
+ } else {
+ int need_fixing = 0;
+
+ cxt->geom.heads = SSWAP16(sunlabel->nhead);
+ cxt->geom.cylinders = SSWAP16(sunlabel->ncyl);
+ cxt->geom.sectors = SSWAP16(sunlabel->nsect);
+
+ if (sunlabel->version != SSWAP32(SUN_LABEL_VERSION)) {
+ fprintf(stderr,_("Detected sun disklabel with wrong version [0x%08x].\n"),
+ SSWAP32(sunlabel->version));
+ need_fixing = 1;
+ }
+ if (sunlabel->sanity != SSWAP32(SUN_LABEL_SANE)) {
+ fprintf(stderr,_("Detected sun disklabel with wrong sanity [0x%08x].\n"),
+ SSWAP32(sunlabel->sanity));
+ need_fixing = 1;
+ }
+ if (sunlabel->num_partitions != SSWAP16(SUN_NUM_PARTITIONS)) {
+ fprintf(stderr,_("Detected sun disklabel with wrong num_partitions [%u].\n"),
+ SSWAP16(sunlabel->num_partitions));
+ need_fixing = 1;
+ }
+ if (need_fixing) {
+ fprintf(stderr, _("Warning: Wrong values need to be "
+ "fixed up and will be corrected "
+ "by w(rite)\n"));
+ sunlabel->version = SSWAP32(SUN_LABEL_VERSION);
+ sunlabel->sanity = SSWAP32(SUN_LABEL_SANE);
+ sunlabel->num_partitions = SSWAP16(SUN_NUM_PARTITIONS);
+
+ ush = (unsigned short *)sunlabel;
+ csum = 0;
+ while(ush < (unsigned short *)(&sunlabel->cksum))
+ csum ^= *ush++;
+ sunlabel->cksum = csum;
+
+ set_changed(0);
+ }
+ }
+ update_units(cxt);
+ return 1;
+}
+
+static int sun_create_disklabel(struct fdisk_context *cxt)
+{
+ struct hd_geometry geometry;
+ sector_t llsectors, llcyls;
+ unsigned int ndiv, sec_fac;
+ int res;
+
+ fprintf(stderr,
+ _("Building a new Sun disklabel.\n"));
+#if BYTE_ORDER == LITTLE_ENDIAN
+ other_endian = 1;
+#else
+ other_endian = 0;
+#endif
+
+ init();
+ fdisk_zeroize_firstsector(cxt);
+
+ sunlabel->magic = SSWAP16(SUN_LABEL_MAGIC);
+ sunlabel->sanity = SSWAP32(SUN_LABEL_SANE);
+ sunlabel->version = SSWAP32(SUN_LABEL_VERSION);
+ sunlabel->num_partitions = SSWAP16(SUN_NUM_PARTITIONS);
+
+ res = blkdev_get_sectors(cxt->dev_fd, &llsectors);
+ sec_fac = cxt->sector_size / 512;
+
+#ifdef HDIO_GETGEO
+ if (!ioctl(cxt->dev_fd, HDIO_GETGEO, &geometry)) {
+ cxt->geom.heads = geometry.heads;
+ cxt->geom.sectors = geometry.sectors;
+ if (res == 0) {
+ llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+ cxt->geom.cylinders = llcyls;
+ if (cxt->geom.cylinders != llcyls)
+ cxt->geom.cylinders = ~0;
+ } else {
+ cxt->geom.cylinders = geometry.cylinders;
+ fprintf(stderr,
+ _("Warning: BLKGETSIZE ioctl failed on %s. "
+ "Using geometry cylinder value of %llu.\n"
+ "This value may be truncated for devices"
+ " > 33.8 GB.\n"), cxt->dev_path, cxt->geom.cylinders);
+ }
+ } else
+#endif
+ {
+ cxt->geom.heads = read_int(cxt, 1,1,1024,0,_("Heads"));
+ cxt->geom.sectors = read_int(cxt, 1,1,1024,0,_("Sectors/track"));
+ cxt->geom.cylinders = read_int(cxt, 1,1,65535,0,_("Cylinders"));
+ }
+
+ sunlabel->acyl = SSWAP16(2);
+ sunlabel->pcyl = SSWAP16(cxt->geom.cylinders);
+ sunlabel->ncyl = SSWAP16(cxt->geom.cylinders - 2);
+ sunlabel->rpm = SSWAP16(5400);
+ sunlabel->intrlv = SSWAP16(1);
+ sunlabel->apc = SSWAP16(0);
+
+ sunlabel->nhead = SSWAP16(cxt->geom.heads);
+ sunlabel->nsect = SSWAP16(cxt->geom.sectors);
+ sunlabel->ncyl = SSWAP16(cxt->geom.cylinders);
+
+ snprintf(sunlabel->label_id, sizeof(sunlabel->label_id),
+ "Linux cyl %llu alt %d hd %d sec %llu",
+ cxt->geom.cylinders, SSWAP16(sunlabel->acyl), cxt->geom.heads, cxt->geom.sectors);
+
+ if (cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors >= 150 * 2048) {
+ ndiv = cxt->geom.cylinders - (50 * 2048 / (cxt->geom.heads * cxt->geom.sectors)); /* 50M swap */
+ } else
+ ndiv = cxt->geom.cylinders * 2 / 3;
+
+ set_sun_partition(cxt, 0, 0, ndiv * cxt->geom.heads * cxt->geom.sectors,
+ SUN_TAG_LINUX_NATIVE);
+ set_sun_partition(cxt, 1, ndiv * cxt->geom.heads * cxt->geom.sectors,
+ cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+ SUN_TAG_LINUX_SWAP);
+ sunlabel->part_tags[1].flag |= SSWAP16(SUN_FLAG_UNMNT);
+
+ set_sun_partition(cxt, 2, 0, cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors, SUN_TAG_BACKUP);
+
+ {
+ unsigned short *ush = (unsigned short *)sunlabel;
+ unsigned short csum = 0;
+ while(ush < (unsigned short *)(&sunlabel->cksum))
+ csum ^= *ush++;
+ sunlabel->cksum = csum;
+ }
+
+ set_all_unchanged();
+ set_changed(0);
+
+ return 0;
+}
+
+void toggle_sunflags(struct fdisk_context *cxt, int i, uint16_t mask)
+{
+ struct sun_tag_flag *p = &sunlabel->part_tags[i];
+
+ p->flag ^= SSWAP16(mask);
+
+ set_changed(i);
+}
+
+static void fetch_sun(struct fdisk_context *cxt, uint32_t *starts,
+ uint32_t *lens, uint32_t *start, uint32_t *stop)
+{
+ int i, continuous = 1;
+
+ *start = 0;
+ *stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+
+ for (i = 0; i < partitions; i++) {
+ struct sun_partition *part = &sunlabel->partitions[i];
+ struct sun_tag_flag *tag = &sunlabel->part_tags[i];
+
+ if (part->num_sectors &&
+ tag->tag != SSWAP16(SUN_TAG_UNASSIGNED) &&
+ tag->tag != SSWAP16(SUN_TAG_BACKUP)) {
+ starts[i] = (SSWAP32(part->start_cylinder) *
+ cxt->geom.heads * cxt->geom.sectors);
+ lens[i] = SSWAP32(part->num_sectors);
+ if (continuous) {
+ if (starts[i] == *start)
+ *start += lens[i];
+ else if (starts[i] + lens[i] >= *stop)
+ *stop = starts[i];
+ else
+ continuous = 0;
+ /* There will be probably more gaps
+ than one, so lets check afterwards */
+ }
+ } else {
+ starts[i] = 0;
+ lens[i] = 0;
+ }
+ }
+}
+
+static unsigned int *verify_sun_starts;
+
+static int verify_sun_cmp(int *a, int *b)
+{
+ if (*a == -1)
+ return 1;
+ if (*b == -1)
+ return -1;
+ if (verify_sun_starts[*a] > verify_sun_starts[*b])
+ return 1;
+ return -1;
+}
+
+static int sun_verify_disklabel(struct fdisk_context *cxt)
+{
+ uint32_t starts[SUN_NUM_PARTITIONS], lens[SUN_NUM_PARTITIONS], start, stop;
+ uint32_t i,j,k,starto,endo;
+ int array[SUN_NUM_PARTITIONS];
+
+ verify_sun_starts = starts;
+
+ fetch_sun(cxt, starts, lens, &start, &stop);
+
+ for (k = 0; k < 7; k++) {
+ for (i = 0; i < SUN_NUM_PARTITIONS; i++) {
+ if (k && (lens[i] % (cxt->geom.heads * cxt->geom.sectors))) {
+ printf(_("Partition %d doesn't end on cylinder boundary\n"), i+1);
+ }
+ if (lens[i]) {
+ for (j = 0; j < i; j++)
+ if (lens[j]) {
+ if (starts[j] == starts[i]+lens[i]) {
+ starts[j] = starts[i]; lens[j] += lens[i];
+ lens[i] = 0;
+ } else if (starts[i] == starts[j]+lens[j]){
+ lens[j] += lens[i];
+ lens[i] = 0;
+ } else if (!k) {
+ if (starts[i] < starts[j]+lens[j] &&
+ starts[j] < starts[i]+lens[i]) {
+ starto = starts[i];
+ if (starts[j] > starto)
+ starto = starts[j];
+ endo = starts[i]+lens[i];
+ if (starts[j]+lens[j] < endo)
+ endo = starts[j]+lens[j];
+ printf(_("Partition %d overlaps with others in "
+ "sectors %d-%d\n"), i+1, starto, endo);
+ }
+ }
+ }
+ }
+ }
+ }
+ for (i = 0; i < SUN_NUM_PARTITIONS; i++) {
+ if (lens[i])
+ array[i] = i;
+ else
+ array[i] = -1;
+ }
+ qsort(array,ARRAY_SIZE(array),sizeof(array[0]),
+ (int (*)(const void *,const void *)) verify_sun_cmp);
+
+ if (array[0] == -1) {
+ printf(_("No partitions defined\n"));
+ return 0;
+ }
+ stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+ if (starts[array[0]])
+ printf(_("Unused gap - sectors 0-%d\n"), starts[array[0]]);
+ for (i = 0; i < 7 && array[i+1] != -1; i++) {
+ printf(_("Unused gap - sectors %d-%d\n"),
+ (starts[array[i]] + lens[array[i]]),
+ starts[array[i+1]]);
+ }
+ start = (starts[array[i]] + lens[array[i]]);
+ if (start < stop)
+ printf(_("Unused gap - sectors %d-%d\n"), start, stop);
+
+ return 0;
+}
+
+static void sun_add_partition(struct fdisk_context *cxt, int n, int sys)
+{
+ uint32_t starts[SUN_NUM_PARTITIONS], lens[SUN_NUM_PARTITIONS];
+ struct sun_partition *part = &sunlabel->partitions[n];
+ struct sun_tag_flag *tag = &sunlabel->part_tags[n];
+ uint32_t start, stop, stop2;
+ int whole_disk = 0;
+
+ char mesg[256];
+ int i;
+ unsigned int first, last;
+
+ if (part->num_sectors && tag->tag != SSWAP16(SUN_TAG_UNASSIGNED)) {
+ printf(_("Partition %d is already defined. Delete "
+ "it before re-adding it.\n"), n + 1);
+ return;
+ }
+
+ fetch_sun(cxt, starts, lens, &start, &stop);
+
+ if (stop <= start) {
+ if (n == 2)
+ whole_disk = 1;
+ else {
+ printf(_("Other partitions already cover the whole disk.\nDelete "
+ "some/shrink them before retry.\n"));
+ return;
+ }
+ }
+ snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
+ for (;;) {
+ if (whole_disk)
+ first = read_int(cxt, 0, 0, 0, 0, mesg);
+ else
+ first = read_int(cxt, scround(start), scround(stop)+1,
+ scround(stop), 0, mesg);
+ if (display_in_cyl_units)
+ first *= units_per_sector;
+ else {
+ /* Starting sector has to be properly aligned */
+ int cs = cxt->geom.heads * cxt->geom.sectors;
+ int x = first % cs;
+
+ if (x)
+ first += cs - x;
+ }
+ if (n == 2 && first != 0)
+ printf (_("\
+It is highly recommended that the third partition covers the whole disk\n\
+and is of type `Whole disk'\n"));
+ /* ewt asks to add: "don't start a partition at cyl 0"
+ However, edmundo@rano.demon.co.uk writes:
+ "In addition to having a Sun partition table, to be able to
+ boot from the disc, the first partition, /dev/sdX1, must
+ start at cylinder 0. This means that /dev/sdX1 contains
+ the partition table and the boot block, as these are the
+ first two sectors of the disc. Therefore you must be
+ careful what you use /dev/sdX1 for. In particular, you must
+ not use a partition starting at cylinder 0 for Linux swap,
+ as that would overwrite the partition table and the boot
+ block. You may, however, use such a partition for a UFS
+ or EXT2 file system, as these file systems leave the first
+ 1024 bytes undisturbed. */
+ /* On the other hand, one should not use partitions
+ starting at block 0 in an md, or the label will
+ be trashed. */
+ for (i = 0; i < partitions; i++)
+ if (lens[i] && starts[i] <= first
+ && starts[i] + lens[i] > first)
+ break;
+ if (i < partitions && !whole_disk) {
+ if (n == 2 && !first) {
+ whole_disk = 1;
+ break;
+ }
+ printf(_("Sector %d is already allocated\n"), first);
+ } else
+ break;
+ }
+ stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors; /* ancient */
+ stop2 = stop;
+ for (i = 0; i < partitions; i++) {
+ if (starts[i] > first && starts[i] < stop)
+ stop = starts[i];
+ }
+ snprintf(mesg, sizeof(mesg),
+ _("Last %s or +size or +sizeM or +sizeK"),
+ str_units(SINGULAR));
+ if (whole_disk)
+ last = read_int(cxt, scround(stop2), scround(stop2), scround(stop2),
+ 0, mesg);
+ else if (n == 2 && !first)
+ last = read_int(cxt, scround(first), scround(stop2), scround(stop2),
+ scround(first), mesg);
+ else
+ last = read_int(cxt, scround(first), scround(stop), scround(stop),
+ scround(first), mesg);
+ if (display_in_cyl_units)
+ last *= units_per_sector;
+ if (n == 2 && !first) {
+ if (last >= stop2) {
+ whole_disk = 1;
+ last = stop2;
+ } else if (last > stop) {
+ printf (
+ _("You haven't covered the whole disk with the 3rd partition, but your value\n"
+ "%d %s covers some other partition. Your entry has been changed\n"
+ "to %d %s\n"),
+ scround(last), str_units(SINGULAR),
+ scround(stop), str_units(SINGULAR));
+ last = stop;
+ }
+ } else if (!whole_disk && last > stop)
+ last = stop;
+
+ if (whole_disk)
+ sys = SUN_TAG_BACKUP;
+
+ set_sun_partition(cxt, n, first, last, sys);
+}
+
+static void sun_delete_partition(struct fdisk_context *cxt, int partnum)
+{
+ struct sun_partition *part = &sunlabel->partitions[partnum];
+ struct sun_tag_flag *tag = &sunlabel->part_tags[partnum];
+ unsigned int nsec;
+
+ if (partnum == 2 &&
+ tag->tag == SSWAP16(SUN_TAG_BACKUP) &&
+ !part->start_cylinder &&
+ (nsec = SSWAP32(part->num_sectors))
+ == cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders)
+ printf(_("If you want to maintain SunOS/Solaris compatibility, "
+ "consider leaving this\n"
+ "partition as Whole disk (5), starting at 0, with %u "
+ "sectors\n"), nsec);
+ tag->tag = SSWAP16(SUN_TAG_UNASSIGNED);
+ part->num_sectors = 0;
+}
+
+int sun_change_sysid(struct fdisk_context *cxt, int i, uint16_t sys)
+{
+ struct sun_partition *part = &sunlabel->partitions[i];
+ struct sun_tag_flag *tag = &sunlabel->part_tags[i];
+
+ if (sys == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
+ read_chars(
+ _("It is highly recommended that the partition at offset 0\n"
+ "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+ "there may destroy your partition table and bootblock.\n"
+ "Type YES if you're very sure you would like that partition\n"
+ "tagged with 82 (Linux swap): "));
+ if (strcmp (line_ptr, _("YES\n")))
+ return 0;
+ }
+ switch (sys) {
+ case SUN_TAG_SWAP:
+ case SUN_TAG_LINUX_SWAP:
+ /* swaps are not mountable by default */
+ tag->flag |= SSWAP16(SUN_FLAG_UNMNT);
+ break;
+ default:
+ /* assume other types are mountable;
+ user can change it anyway */
+ tag->flag &= ~SSWAP16(SUN_FLAG_UNMNT);
+ break;
+ }
+ tag->tag = SSWAP16(sys);
+ return 1;
+}
+
+void sun_list_table(struct fdisk_context *cxt, int xtra)
+{
+ int i, w;
+ char *type;
+
+ w = strlen(cxt->dev_path);
+ if (xtra)
+ printf(
+ _("\nDisk %s (Sun disk label): %u heads, %llu sectors, %d rpm\n"
+ "%llu cylinders, %d alternate cylinders, %d physical cylinders\n"
+ "%d extra sects/cyl, interleave %d:1\n"
+ "Label ID: %s\n"
+ "Volume ID: %s\n"
+ "Units = %s of %d * 512 bytes\n\n"),
+ cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, SSWAP16(sunlabel->rpm),
+ cxt->geom.cylinders, SSWAP16(sunlabel->acyl),
+ SSWAP16(sunlabel->pcyl),
+ SSWAP16(sunlabel->apc),
+ SSWAP16(sunlabel->intrlv),
+ sunlabel->label_id,
+ sunlabel->volume_id,
+ str_units(PLURAL), units_per_sector);
+ else
+ printf(
+ _("\nDisk %s (Sun disk label): %u heads, %llu sectors, %llu cylinders\n"
+ "Units = %s of %d * 512 bytes\n\n"),
+ cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders,
+ str_units(PLURAL), units_per_sector);
+
+ printf(_("%*s Flag Start End Blocks Id System\n"),
+ w + 1, _("Device"));
+ for (i = 0 ; i < partitions; i++) {
+ struct sun_partition *part = &sunlabel->partitions[i];
+ struct sun_tag_flag *tag = &sunlabel->part_tags[i];
+
+ if (part->num_sectors) {
+ uint32_t start = SSWAP32(part->start_cylinder) * cxt->geom.heads * cxt->geom.sectors;
+ uint32_t len = SSWAP32(part->num_sectors);
+ printf(
+ "%s %c%c %9lu %9lu %9lu%c %2x %s\n",
+/* device */ partname(cxt->dev_path, i+1, w),
+/* flags */ (tag->flag & SSWAP16(SUN_FLAG_UNMNT)) ? 'u' : ' ',
+ (tag->flag & SSWAP16(SUN_FLAG_RONLY)) ? 'r' : ' ',
+/* start */ (unsigned long) scround(start),
+/* end */ (unsigned long) scround(start+len),
+/* odd flag on end */ (unsigned long) len / 2, len & 1 ? '+' : ' ',
+/* type id */ SSWAP16(tag->tag),
+/* type name */ (type = partition_type(SSWAP16(tag->tag)))
+ ? type : _("Unknown"));
+ }
+ }
+}
+
+void sun_set_alt_cyl(struct fdisk_context *cxt)
+{
+ sunlabel->acyl =
+ SSWAP16(read_int(cxt, 0,SSWAP16(sunlabel->acyl), 65535, 0,
+ _("Number of alternate cylinders")));
+}
+
+void sun_set_ncyl(struct fdisk_context *cxt, int cyl)
+{
+ sunlabel->ncyl = SSWAP16(cyl);
+}
+
+void sun_set_xcyl(struct fdisk_context *cxt)
+{
+ sunlabel->apc =
+ SSWAP16(read_int(cxt, 0, SSWAP16(sunlabel->apc), cxt->geom.sectors, 0,
+ _("Extra sectors per cylinder")));
+}
+
+void sun_set_ilfact(struct fdisk_context *cxt)
+{
+ sunlabel->intrlv =
+ SSWAP16(read_int(cxt, 1, SSWAP16(sunlabel->intrlv), 32, 0,
+ _("Interleave factor")));
+}
+
+void sun_set_rspeed(struct fdisk_context *cxt)
+{
+ sunlabel->rpm =
+ SSWAP16(read_int(cxt, 1, SSWAP16(sunlabel->rpm), 100000, 0,
+ _("Rotation speed (rpm)")));
+}
+
+void sun_set_pcylcount(struct fdisk_context *cxt)
+{
+ sunlabel->pcyl =
+ SSWAP16(read_int(cxt, 0, SSWAP16(sunlabel->pcyl), 65535, 0,
+ _("Number of physical cylinders")));
+}
+
+static int sun_write_disklabel(struct fdisk_context *cxt)
+{
+ unsigned short *ush = (unsigned short *)sunlabel;
+ unsigned short csum = 0;
+
+ while(ush < (unsigned short *)(&sunlabel->cksum))
+ csum ^= *ush++;
+ sunlabel->cksum = csum;
+ if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+ return -errno;
+ if (write(cxt->dev_fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE)
+ return -errno;
+
+ return 0;
+}
+
+int sun_get_sysid(struct fdisk_context *cxt, int i)
+{
+ return SSWAP16(sunlabel->part_tags[i].tag);
+}
+
+const struct fdisk_label sun_label =
+{
+ .name = "sun",
+ .probe = sun_probe_label,
+ .write = sun_write_disklabel,
+ .verify = sun_verify_disklabel,
+ .create = sun_create_disklabel,
+ .part_add = sun_add_partition,
+ .part_delete = sun_delete_partition,
+};
diff --git a/fdisks/fdisksunlabel.h b/fdisks/fdisksunlabel.h
new file mode 100644
index 0000000..12cccb1
--- /dev/null
+++ b/fdisks/fdisksunlabel.h
@@ -0,0 +1,91 @@
+#ifndef FDISK_SUN_LABEL_H
+#define FDISK_SUN_LABEL_H
+
+#include <stdint.h>
+
+struct sun_partition {
+ uint32_t start_cylinder;
+ uint32_t num_sectors;
+};
+
+struct sun_tag_flag {
+ uint16_t tag;
+#define SUN_TAG_UNASSIGNED 0x00 /* Unassigned partition */
+#define SUN_TAG_BOOT 0x01 /* Boot partition */
+#define SUN_TAG_ROOT 0x02 /* Root filesystem */
+#define SUN_TAG_SWAP 0x03 /* Swap partition */
+#define SUN_TAG_USR 0x04 /* /usr filesystem */
+#define SUN_TAG_BACKUP 0x05 /* Full-disk slice */
+#define SUN_TAG_STAND 0x06 /* Stand partition */
+#define SUN_TAG_VAR 0x07 /* /var filesystem */
+#define SUN_TAG_HOME 0x08 /* /home filesystem */
+#define SUN_TAG_ALTSCTR 0x09 /* Alt sector partition */
+#define SUN_TAG_CACHE 0x0a /* Cachefs partition */
+#define SUN_TAG_RESERVED 0x0b /* SMI reserved data */
+#define SUN_TAG_LINUX_SWAP 0x82 /* Linux SWAP */
+#define SUN_TAG_LINUX_NATIVE 0x83 /* Linux filesystem */
+#define SUN_TAG_LINUX_LVM 0x8e /* Linux LVM */
+#define SUN_TAG_LINUX_RAID 0xfd /* LInux RAID */
+
+ uint16_t flag;
+#define SUN_FLAG_UNMNT 0x01 /* Unmountable partition*/
+#define SUN_FLAG_RONLY 0x10 /* Read only */
+};
+
+#define SUN_LABEL_SIZE 512
+
+#define SUN_LABEL_ID_SIZE 128
+#define SUN_VOLUME_ID_SIZE 8
+
+#define SUN_LABEL_VERSION 0x00000001
+#define SUN_LABEL_SANE 0x600ddeee
+#define SUN_NUM_PARTITIONS 8
+
+struct sun_disk_label {
+ char label_id[SUN_LABEL_ID_SIZE];
+ uint32_t version;
+ char volume_id[SUN_VOLUME_ID_SIZE];
+ uint16_t num_partitions;
+ struct sun_tag_flag part_tags[SUN_NUM_PARTITIONS];
+ uint32_t bootinfo[3];
+ uint32_t sanity;
+ uint32_t resv[10];
+ uint32_t part_timestamps[SUN_NUM_PARTITIONS];
+ uint32_t write_reinstruct;
+ uint32_t read_reinstruct;
+ uint8_t pad[148];
+ uint16_t rpm;
+ uint16_t pcyl;
+ uint16_t apc;
+ uint16_t resv1;
+ uint16_t resv2;
+ uint16_t intrlv;
+ uint16_t ncyl;
+ uint16_t acyl;
+ uint16_t nhead;
+ uint16_t nsect;
+ uint16_t resv3;
+ uint16_t resv4;
+ struct sun_partition partitions[SUN_NUM_PARTITIONS];
+ uint16_t magic;
+ uint16_t cksum;
+};
+
+#define SUN_LABEL_MAGIC 0xDABE
+#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA
+#define sunlabel ((struct sun_disk_label *)cxt->firstsector)
+
+/* fdisksunlabel.c */
+extern struct systypes sun_sys_types[];
+extern int sun_change_sysid(struct fdisk_context *cxt, int i, uint16_t sys);
+extern void sun_list_table(struct fdisk_context *cxt, int xtra);
+extern void sun_set_alt_cyl(struct fdisk_context *cxt);
+extern void sun_set_ncyl(struct fdisk_context *cxt, int cyl);
+extern void sun_set_xcyl(struct fdisk_context *cxt);
+extern void sun_set_ilfact(struct fdisk_context *cxt);
+extern void sun_set_rspeed(struct fdisk_context *cxt);
+extern void sun_set_pcylcount(struct fdisk_context *cxt);
+extern void toggle_sunflags(struct fdisk_context *cxt, int i, uint16_t mask);
+extern int sun_get_sysid(struct fdisk_context *cxt, int i);
+
+#endif /* FDISK_SUN_LABEL_H */
diff --git a/fdisks/gpt.c b/fdisks/gpt.c
new file mode 100644
index 0000000..bb6911a
--- /dev/null
+++ b/fdisks/gpt.c
@@ -0,0 +1,216 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ *
+ * GPT (GUID Partition Table) signature detection. Based on libparted and
+ * util-linux/partx.
+ *
+ * Warning: this code doesn't do all GPT checks (CRC32, Protective MBR, ..).
+ * It's really GPT signature detection only.
+ *
+ * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "gpt.h"
+#include "blkdev.h"
+#include "bitops.h"
+#include "closestream.h"
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645LL
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+typedef struct {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_hi_and_reserved;
+ uint8_t clock_seq_low;
+ uint8_t node[6];
+} /* __attribute__ ((packed)) */ efi_guid_t;
+/* commented out "__attribute__ ((packed))" to work around gcc bug (fixed
+ * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized
+ * data. It turns out we don't need it in this case, so it doesn't break
+ * anything :)
+ */
+
+typedef struct _GuidPartitionTableHeader_t {
+ uint64_t Signature;
+ uint32_t Revision;
+ uint32_t HeaderSize;
+ uint32_t HeaderCRC32;
+ uint32_t Reserved1;
+ uint64_t MyLBA;
+ uint64_t AlternateLBA;
+ uint64_t FirstUsableLBA;
+ uint64_t LastUsableLBA;
+ efi_guid_t DiskGUID;
+ uint64_t PartitionEntryLBA;
+ uint32_t NumberOfPartitionEntries;
+ uint32_t SizeOfPartitionEntry;
+ uint32_t PartitionEntryArrayCRC32;
+ uint8_t Reserved2[512 - 92];
+} __attribute__ ((packed)) GuidPartitionTableHeader_t;
+
+static int
+_get_sector_size (int fd)
+{
+ int sector_size;
+
+ if (blkdev_get_sector_size(fd, &sector_size) == -1)
+ return DEFAULT_SECTOR_SIZE;
+ return sector_size;
+}
+
+static uint64_t
+_get_num_sectors(int fd)
+{
+ unsigned long long bytes=0;
+
+ if (blkdev_get_size(fd, &bytes) == -1)
+ return 0;
+ return bytes / _get_sector_size(fd);
+}
+
+static uint64_t
+last_lba(int fd)
+{
+ int rc;
+ uint64_t sectors = 0;
+ struct stat s;
+
+ memset(&s, 0, sizeof (s));
+ rc = fstat(fd, &s);
+ if (rc == -1)
+ {
+ fprintf(stderr, "last_lba() could not stat: %m\n");
+ return 0;
+ }
+ if (S_ISBLK(s.st_mode))
+ sectors = _get_num_sectors(fd);
+ else if (S_ISREG(s.st_mode))
+ sectors = s.st_size >> _get_sector_size(fd);
+ else
+ {
+ fprintf(stderr,
+ "last_lba(): I don't know how to handle files with mode %o\n",
+ s.st_mode);
+ sectors = 1;
+ }
+ return sectors - 1;
+}
+
+static ssize_t
+read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
+{
+ int sector_size = _get_sector_size(fd);
+ off_t offset = lba * sector_size;
+
+ lseek(fd, offset, SEEK_SET);
+ return read(fd, buffer, bytes);
+}
+
+static GuidPartitionTableHeader_t *
+alloc_read_gpt_header(int fd, uint64_t lba)
+{
+ GuidPartitionTableHeader_t *gpt =
+ (GuidPartitionTableHeader_t *) malloc(sizeof (GuidPartitionTableHeader_t));
+ if (!gpt)
+ return NULL;
+ memset(gpt, 0, sizeof (*gpt));
+ if (!read_lba(fd, lba, gpt, sizeof (GuidPartitionTableHeader_t)))
+ {
+ free(gpt);
+ return NULL;
+ }
+ return gpt;
+}
+
+static int
+gpt_check_signature(int fd, uint64_t lba)
+{
+ GuidPartitionTableHeader_t *gpt;
+ int res=0;
+
+ if ((gpt = alloc_read_gpt_header(fd, lba)))
+ {
+ if (gpt->Signature == cpu_to_le64(GPT_HEADER_SIGNATURE))
+ res = 1;
+ free(gpt);
+ }
+ return res;
+}
+
+/* returns:
+ * 0 not found GPT
+ * 1 for valid primary GPT header
+ * 2 for valid alternative GPT header
+ */
+int
+gpt_probe_signature_fd(int fd)
+{
+ int res = 0;
+
+ /* check primary GPT header */
+ if (gpt_check_signature(fd, GPT_PRIMARY_PARTITION_TABLE_LBA))
+ res = 1;
+ else
+ {
+ /* check alternative GPT header */
+ uint64_t lastlba = last_lba(fd);
+ if (gpt_check_signature(fd, lastlba))
+ res = 2;
+ }
+ return res;
+}
+
+int
+gpt_probe_signature_devname(char *devname)
+{
+ int res, fd;
+ if ((fd = open(devname, O_RDONLY)) < 0)
+ return 0;
+ res = gpt_probe_signature_fd(fd);
+ close(fd);
+ return res;
+}
+
+#ifdef GPT_TEST_MAIN
+int
+main(int argc, char **argv)
+{
+ atexit(close_stdout);
+ if (argc!=2)
+ {
+ fprintf(stderr, "usage: %s <dev>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ if (gpt_probe_signature_devname(argv[1]))
+ printf("GPT (GUID Partition Table) detected on %s\n", argv[1]);
+ exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/fdisks/gpt.h b/fdisks/gpt.h
new file mode 100644
index 0000000..2ac21c4
--- /dev/null
+++ b/fdisks/gpt.h
@@ -0,0 +1,7 @@
+#ifndef FDISK_GPT_H
+#define FDISK_GPT_H
+
+extern int gpt_probe_signature_fd(int fd);
+extern int gpt_probe_signature_devname(char *devname);
+
+#endif /* FDISK_GPT_H */
diff --git a/fdisks/i386_sys_types.c b/fdisks/i386_sys_types.c
new file mode 100644
index 0000000..916daf6
--- /dev/null
+++ b/fdisks/i386_sys_types.c
@@ -0,0 +1,110 @@
+/* DOS partition types */
+#include "common.h"
+#include "nls.h"
+
+struct systypes i386_sys_types[] = {
+ {0x00, N_("Empty")},
+ {0x01, N_("FAT12")},
+ {0x02, N_("XENIX root")},
+ {0x03, N_("XENIX usr")},
+ {0x04, N_("FAT16 <32M")},
+ {0x05, N_("Extended")}, /* DOS 3.3+ extended partition */
+ {0x06, N_("FAT16")}, /* DOS 16-bit >=32M */
+ {0x07, N_("HPFS/NTFS/exFAT")}, /* OS/2 IFS, eg, HPFS or NTFS or QNX or exFAT */
+ {0x08, N_("AIX")}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ {0x09, N_("AIX bootable")}, /* AIX data or Coherent */
+ {0x0a, N_("OS/2 Boot Manager")},/* OS/2 Boot Manager */
+ {0x0b, N_("W95 FAT32")},
+ {0x0c, N_("W95 FAT32 (LBA)")},/* LBA really is `Extended Int 13h' */
+ {0x0e, N_("W95 FAT16 (LBA)")},
+ {0x0f, N_("W95 Ext'd (LBA)")},
+ {0x10, N_("OPUS")},
+ {0x11, N_("Hidden FAT12")},
+ {0x12, N_("Compaq diagnostics")},
+ {0x14, N_("Hidden FAT16 <32M")},
+ {0x16, N_("Hidden FAT16")},
+ {0x17, N_("Hidden HPFS/NTFS")},
+ {0x18, N_("AST SmartSleep")},
+ {0x1b, N_("Hidden W95 FAT32")},
+ {0x1c, N_("Hidden W95 FAT32 (LBA)")},
+ {0x1e, N_("Hidden W95 FAT16 (LBA)")},
+ {0x24, N_("NEC DOS")},
+ {0x27, N_("Hidden NTFS WinRE")},
+ {0x39, N_("Plan 9")},
+ {0x3c, N_("PartitionMagic recovery")},
+ {0x40, N_("Venix 80286")},
+ {0x41, N_("PPC PReP Boot")},
+ {0x42, N_("SFS")},
+ {0x4d, N_("QNX4.x")},
+ {0x4e, N_("QNX4.x 2nd part")},
+ {0x4f, N_("QNX4.x 3rd part")},
+ {0x50, N_("OnTrack DM")},
+ {0x51, N_("OnTrack DM6 Aux1")}, /* (or Novell) */
+ {0x52, N_("CP/M")}, /* CP/M or Microport SysV/AT */
+ {0x53, N_("OnTrack DM6 Aux3")},
+ {0x54, N_("OnTrackDM6")},
+ {0x55, N_("EZ-Drive")},
+ {0x56, N_("Golden Bow")},
+ {0x5c, N_("Priam Edisk")},
+ {0x61, N_("SpeedStor")},
+ {0x63, N_("GNU HURD or SysV")}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ {0x64, N_("Novell Netware 286")},
+ {0x65, N_("Novell Netware 386")},
+ {0x70, N_("DiskSecure Multi-Boot")},
+ {0x75, N_("PC/IX")},
+ {0x80, N_("Old Minix")}, /* Minix 1.4a and earlier */
+ {0x81, N_("Minix / old Linux")},/* Minix 1.4b and later */
+ {0x82, N_("Linux swap / Solaris")},
+ {0x83, N_("Linux")},
+ {0x84, N_("OS/2 hidden C: drive")},
+ {0x85, N_("Linux extended")},
+ {0x86, N_("NTFS volume set")},
+ {0x87, N_("NTFS volume set")},
+ {0x88, N_("Linux plaintext")},
+ {0x8e, N_("Linux LVM")},
+ {0x93, N_("Amoeba")},
+ {0x94, N_("Amoeba BBT")}, /* (bad block table) */
+ {0x9f, N_("BSD/OS")}, /* BSDI */
+ {0xa0, N_("IBM Thinkpad hibernation")},
+ {0xa5, N_("FreeBSD")}, /* various BSD flavours */
+ {0xa6, N_("OpenBSD")},
+ {0xa7, N_("NeXTSTEP")},
+ {0xa8, N_("Darwin UFS")},
+ {0xa9, N_("NetBSD")},
+ {0xab, N_("Darwin boot")},
+ {0xaf, N_("HFS / HFS+")},
+ {0xb7, N_("BSDI fs")},
+ {0xb8, N_("BSDI swap")},
+ {0xbb, N_("Boot Wizard hidden")},
+ {0xbe, N_("Solaris boot")},
+ {0xbf, N_("Solaris")},
+ {0xc1, N_("DRDOS/sec (FAT-12)")},
+ {0xc4, N_("DRDOS/sec (FAT-16 < 32M)")},
+ {0xc6, N_("DRDOS/sec (FAT-16)")},
+ {0xc7, N_("Syrinx")},
+ {0xda, N_("Non-FS data")},
+ {0xdb, N_("CP/M / CTOS / ...")},/* CP/M or Concurrent CP/M or
+ Concurrent DOS or CTOS */
+ {0xde, N_("Dell Utility")}, /* Dell PowerEdge Server utilities */
+ {0xdf, N_("BootIt")}, /* BootIt EMBRM */
+ {0xe1, N_("DOS access")}, /* DOS access or SpeedStor 12-bit FAT
+ extended partition */
+ {0xe3, N_("DOS R/O")}, /* DOS R/O or SpeedStor */
+ {0xe4, N_("SpeedStor")}, /* SpeedStor 16-bit FAT extended
+ partition < 1024 cyl. */
+ {0xeb, N_("BeOS fs")},
+ {0xee, N_("GPT")}, /* Intel EFI GUID Partition Table */
+ {0xef, N_("EFI (FAT-12/16/32)")},/* Intel EFI System Partition */
+ {0xf0, N_("Linux/PA-RISC boot")},/* Linux/PA-RISC boot loader */
+ {0xf1, N_("SpeedStor")},
+ {0xf4, N_("SpeedStor")}, /* SpeedStor large partition */
+ {0xf2, N_("DOS secondary")}, /* DOS 3.3+ secondary */
+ {0xfb, N_("VMware VMFS")},
+ {0xfc, N_("VMware VMKCORE")}, /* VMware kernel dump partition */
+ {0xfd, N_("Linux raid autodetect")},/* New (2.2.x) raid partition with
+ autodetect using persistent
+ superblock */
+ {0xfe, N_("LANstep")}, /* SpeedStor >1024 cyl. or LANstep */
+ {0xff, N_("BBT")}, /* Xenix Bad Block Table */
+ { 0, 0 }
+};
diff --git a/fdisks/partname.c b/fdisks/partname.c
new file mode 100644
index 0000000..1671e19
--- /dev/null
+++ b/fdisks/partname.c
@@ -0,0 +1,48 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "blkdev.h"
+#include "pathnames.h"
+#include "common.h"
+#include "c.h"
+
+/*
+ * return partition name - uses static storage unless buf is supplied
+ */
+char *
+partname(char *dev, int pno, int lth) {
+ static char bufp[PATH_MAX];
+ char *p;
+ int w, wp;
+
+ w = strlen(dev);
+ p = "";
+
+ if (isdigit(dev[w-1]))
+ p = "p";
+
+ /* devfs kludge - note: fdisk partition names are not supposed
+ to equal kernel names, so there is no reason to do this */
+ if (strcmp (dev + w - 4, "disc") == 0) {
+ w -= 4;
+ p = "part";
+ }
+
+ /* udev names partitions by appending -partN
+ e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */
+ if ((strncmp(dev, _PATH_DEV_BYID, strlen(_PATH_DEV_BYID)) == 0) ||
+ strncmp(dev, _PATH_DEV_BYPATH, strlen(_PATH_DEV_BYPATH)) == 0) {
+ p = "-part";
+ }
+
+ wp = strlen(p);
+
+ if (lth) {
+ snprintf(bufp, sizeof(bufp), "%*.*s%s%-2u",
+ lth-wp-2, w, dev, p, pno);
+ } else {
+ snprintf(bufp, sizeof(bufp), "%.*s%s%-2u", w, dev, p, pno);
+ }
+ return bufp;
+}
diff --git a/fdisks/sfdisk.8 b/fdisks/sfdisk.8
new file mode 100644
index 0000000..32907e3
--- /dev/null
+++ b/fdisks/sfdisk.8
@@ -0,0 +1,603 @@
+.\" Copyright 1995 Andries E. Brouwer (aeb@cwi.nl)
+.\" May be distributed under the GNU General Public License
+.\" The `DOS 6.x Warning' was taken from the old fdisk.8, which says
+.\" -- Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" -- May be distributed under the GNU General Public License
+.\" The `DRDOS Warning' was taken from a net post by Stephen Tweedie.
+.\"
+.TH SFDISK 8 "August 2011" "util-linux" "System Administration"
+.SH NAME
+sfdisk \- partition table manipulator for Linux
+.SH SYNOPSIS
+.B sfdisk
+.RI [ options ]
+.I device
+.br
+.B sfdisk \-s
+.RI [ partition ]
+.SH DESCRIPTION
+.B sfdisk
+has four (main) uses: list the size of a partition, list the partitions
+on a device, check the partitions on a device, and \- very dangerous \-
+repartition a device.
+
+.B sfdisk
+doesn't understand the GUID Partition Table (GPT) format and it is not
+designed for large partitions. In these cases use the more advanced GNU
+.BR parted (8).
+
+Note that
+.B sfdisk
+does not align partitions to block device I/O limits. This functionality is
+provided by
+.BR fdisk (8).
+
+.SS "List sizes"
+.BI "sfdisk \-s " partition
+gives the size of
+.I partition
+in blocks. This may be useful in connection with programs like
+.BR mkswap (8).
+Here
+.I partition
+is usually something like
+.I /dev/hda1
+or
+.IR /dev/sdb12 ,
+but may also be an entire disk, like
+.IR /dev/xda .
+
+.RS
+.nf
+.if t .ft CW
+% sfdisk \-s /dev/hda9
+81599
+.if t .ft R
+.fi
+.RE
+
+If the partition argument is omitted,
+.B sfdisk
+will list the sizes of all disks, and the total:
+
+.RS
+.nf
+.if t .ft CW
+% sfdisk \-s
+/dev/hda: 208896
+/dev/hdb: 1025136
+/dev/hdc: 1031063
+/dev/sda: 8877895
+/dev/sdb: 1758927
+total: 12901917 blocks
+.if t .ft R
+.fi
+.RE
+
+.SS "List partitions"
+The second type of invocation:
+.BI "sfdisk \-l " device
+will list the partitions on the specified device. If the
+.I device
+argument is omitted, the partitions on all hard disks are listed.
+
+.RS
+.nf
+.if t .ft CW
+% sfdisk \-l /dev/hdc
+
+Disk /dev/hdc: 16 heads, 63 sectors, 2045 cylinders
+Units = cylinders of 516096 bytes, blocks of 1024 bytes, counting from 0
+
+ Device Boot Start End #cyls #blocks Id System
+/dev/hdc1 0+ 406 407\- 205096+ 83 Linux native
+/dev/hdc2 407 813 407 205128 83 Linux native
+/dev/hdc3 814 2044 1231 620424 83 Linux native
+/dev/hdc4 0 \- 0 0 0 Empty
+.if t .ft R
+.fi
+.RE
+
+The trailing \- and + signs indicate that rounding has taken place,
+and that the actual value is slightly less or more. To see the
+exact values, ask for a listing with sectors as unit (\fB\-u S\fR).
+
+.SS "Check partitions"
+The third type of invocation:
+.BI "sfdisk \-V " device
+will apply various consistency checks to the partition tables on
+.IR device .
+It prints `OK' or complains. The \fB\-V\fR option can be used
+together with \fB\-l\fR. In a shell script one might use
+.BI "sfdisk \-V \-q " device
+which only returns a status.
+
+.SS "Create partitions"
+The fourth type of invocation:
+.BI "sfdisk " device
+will cause
+.B sfdisk
+to read the specification for the desired partitioning of
+.I device
+from standard input, and then to change the partition tables
+on that disk. Thus it is possible to use
+.B sfdisk
+from a shell script. When
+.B sfdisk
+determines that its standard input is a terminal, it will be
+conversational; otherwise it will abort on any error.
+.LP
+BE EXTREMELY CAREFUL - ONE TYPING MISTAKE AND ALL YOUR DATA IS LOST
+.LP
+As a precaution, one can save the sectors changed by
+.BR sfdisk :
+
+.RS
+.nf
+.if t .ft CW
+% sfdisk /dev/hdd \-O hdd-partition-sectors.save
+\&...
+.if t .ft R
+.fi
+.RE
+
+.LP
+Then, if you discover that you did something stupid before anything
+else has been written to disk, it may be possible to recover
+the old situation with:
+
+.RS
+.nf
+.if t .ft CW
+% sfdisk /dev/hdd \-I hdd-partition-sectors.save
+.if t .ft R
+.fi
+.RE
+
+.LP
+(This is not the same as saving the old partition table:
+a readable version of the old partition table can be saved
+using the \fB\-d\fR option. However, if you create logical partitions,
+the sectors describing them are located somewhere on disk,
+possibly on sectors that were not part of the partition table
+before. Thus, the information the \fB\-O\fR option saves
+is not a binary version of the output of \fB\-d\fR.)
+
+There are many options.
+
+.SH OPTIONS
+.TP
+.BR \-v ", " \-\-version
+Print version number of
+.B sfdisk
+and exit immediately.
+.TP
+.BR \-h ", " \-\-help
+Print a usage message and exit immediately.
+.TP
+.BR \-T ", " \-\-list\-types
+Print the recognized types (system Id's).
+.TP
+.BR \-s ", " \-\-show\-size
+List the size of a partition.
+.TP
+.BR \-g ", " \-\-show\-geometry
+List the kernel's idea of the geometry of the indicated disk(s).
+.TP
+.BR \-G ", " \-\-show\-pt\-geometry
+List the geometry of the indicated disks guessed by looking at
+the partition table.
+.TP
+.BR \-l ", " \-\-list
+List the partitions of a device.
+.TP
+.BR \-d ", " \-\-dump
+Dump the partitions of a device in a format that is usable as input
+to /fBsfdisk/fR. For example,
+.br
+.nf
+.if t .ft CW
+ % sfdisk -d /dev/hda > hda.out
+ % sfdisk /dev/hda < hda.out
+.if t .ft R
+.fi
+will correct the bad last extended partition that the OS/2
+fdisk creates.
+.TP
+.BR \-V ", " \-\-verify
+Test whether partitions seem correct. (See the third invocation type above.)
+.TP
+.BR \-i ", " \-\-increment
+Number cylinders etc. starting from 1 instead of 0.
+.TP
+.BI \-N " number"
+Change only the single partition indicated. For example:
+.nf
+.if t .ft CW
+ % sfdisk /dev/hdb \-N5
+ ,,,*
+.if t .ft R
+.fi
+will make the fifth partition on /dev/hdb bootable (`active')
+and change nothing else. (Probably this fifth partition
+is called /dev/hdb5, but you are free to call it something else,
+like `/my_equipment/disks/2/5' or so).
+.TP
+.BR \-A ", " \-\-activate " \fInumber\fR"
+Make the indicated partition(s) active, and all others inactive.
+.TP
+.BR \-c ", " \-\-id " \fInumber\fR [\fIId\fR]"
+If no \fIId\fR argument given: print the partition Id of the indicated
+partition. If an \fIId\fR argument is present: change the type (Id) of
+the indicated partition to the given value.
+This option has two longer forms, \fB\-\-print\-id\fR and \fB\-\-change\-id\fR.
+For example:
+.br
+.nf
+.if t .ft CW
+ % sfdisk --print-id /dev/hdb 5
+ 6
+ % sfdisk --change-id /dev/hdb 5 83
+ OK
+.if t .ft R
+.fi
+first reports that /dev/hdb5 has Id 6, and then changes that into 83.
+.TP
+.BR \-u ", " \-\-unit " \fIletter\fR"
+Interpret the input and show the output in the units specified by
+.IR letter .
+This \fIletter\fR can be one of S, C, B or M, meaning Sectors, Cylinders,
+Blocks and Megabytes, respectively. The default is
+cylinders, at least when the geometry is known.
+.TP
+.BR \-x ", " \-\-show\-extended
+Also list non-primary extended partitions on output,
+and expect descriptors for them on input.
+.TP
+.BR \-C ", " \-\-cylinders " \fIcylinders\fR"
+Specify the number of cylinders, possibly overriding what the kernel thinks.
+.TP
+.BR \-H ", " \-\-heads " \fIheads\fR"
+Specify the number of heads, possibly overriding what the kernel thinks.
+.TP
+.BR \-S ", " \-\-sectors " \fIsectors\fR"
+Specify the number of sectors, possibly overriding what the kernel thinks.
+.TP
+.BR \-f ", " \-\-force
+Do what I say, even if it is stupid.
+.TP
+.BR \-q ", " \-\-quiet
+Suppress warning messages.
+.TP
+.BR \-L ", " \-\-Linux
+Do not complain about things irrelevant for Linux.
+.TP
+.BR \-D ", " \-\-DOS
+For DOS-compatibility: waste a little space.
+(More precisely: if a partition cannot contain sector 0,
+e.g. because that is the MBR of the device, or contains
+the partition table of an extended partition, then
+.B sfdisk
+would make it start the next sector. However, when this
+option is given it skips to the start of the next track,
+wasting for example 33 sectors (in case of 34 sectors/track),
+just like certain versions of DOS do.)
+Certain Disk Managers and boot loaders (such as OSBS, but not
+LILO or the OS/2 Boot Manager) also live in this empty space,
+so maybe you want this option if you use one.
+.TP
+.BR \-E ", " \-\-DOS\-extended
+Take the starting sector numbers of "inner" extended partitions
+to be relative to the starting cylinder boundary of the outer one
+(like some versions of DOS do), rather than relative to the actual
+starting sector (like Linux does).
+(The fact that there is a difference here means that one should
+always let extended partitions start at cylinder boundaries if
+DOS and Linux should interpret the partition table in the same way.
+Of course one can only know where cylinder boundaries are when
+one knows what geometry DOS will use for this disk.)
+.TP
+.BR \-\-IBM ", " \-\-leave\-last
+Certain IBM diagnostic programs assume that they can use the
+last cylinder on a disk for disk-testing purposes. If you think
+you might ever run such programs, use this option to tell
+.B sfdisk
+that it should not allocate the last cylinder.
+Sometimes the last cylinder contains a bad sector table.
+.TP
+.B \-n
+Go through all the motions, but do not actually write to disk.
+.TP
+.BR \-R ", " \-\-re-read
+Only execute the BLKRRPART ioctl (to make the kernel re-read
+the partition table). This can be useful for checking in advance
+that the final BLKRRPART will be successful, and also when you
+changed the partition table `by hand' (e.g., using dd from a backup).
+If the kernel complains (`device busy for revalidation (usage = 2)')
+then something still uses the device, and you still have to unmount
+some file system, or say swapoff to some swap partition.
+.TP
+.B \-\-no\-reread
+When starting a repartitioning of a disk, \fBsfdisk\fR checks that this disk
+is not mounted, or in use as a swap device, and refuses to continue
+if it is. This option suppresses the test. (On the other hand, the \fB\-f\fR
+option would force \fBsfdisk\fR to continue even when this test fails.)
+.TP
+.B \-\-in\-order
+Caution, see warning section. To be documented.
+.TP
+.B \-\-not\-in\-order
+Caution, see warning section. To be documented.
+.TP
+.B \-\-inside\-outer
+Caution, see warning section. Chaining order.
+.TP
+.B \-\-not\-inside\-outer
+Caution, see warning section. Chaining order.
+.TP
+.B \-\-nested
+Caution, see warning section. Every partition is contained in the
+surrounding partitions and is disjoint from all others.
+.TP
+.B \-\-chained
+Caution, see warning section. Every data partition is contained in
+the surrounding partitions and disjoint from all others, but
+extended partitions may lie outside (insofar as allowed by
+all_logicals_inside_outermost_extended).
+.TP
+.B \-\-onesector
+Caution, see warning section. All data partitions are mutually
+disjoint; extended partitions each use one sector only (except
+perhaps for the outermost one).
+.TP
+.BI \-O " file"
+Just before writing the new partition, output the sectors
+that are going to be overwritten to
+.I file
+(where hopefully
+.I file
+resides on another disk, or on a floppy).
+.TP
+.BI \-I " file"
+After destroying your filesystems with an unfortunate
+.B sfdisk
+command, you would have been able to restore the old situation
+if only you had preserved it using the \fB\-O\fR flag.
+
+.SH THEORY
+Block 0 of a disk (the Master Boot Record) contains among
+other things four partition descriptors. The partitions
+described here are called
+.I primary
+partitions.
+.LP
+A partition descriptor has 6 fields:
+.br
+.nf
+.RS
+struct partition {
+ unsigned char bootable; /* 0 or 0x80 */
+ hsc begin_hsc;
+ unsigned char id;
+ hsc end_hsc;
+ unsigned int starting_sector;
+ unsigned int nr_of_sectors;
+}
+.RE
+.fi
+.LP
+The two hsc fields indicate head, sector and cylinder of the
+begin and the end of the partition. Since each hsc field only
+takes 3 bytes, only 24 bits are available, which does not
+suffice for big disks (say > 8GB). In fact, due to the wasteful
+representation (that uses a byte for the number of heads, which
+is typically 16), problems already start with 0.5GB.
+However Linux does not use these fields, and problems can arise
+only at boot time, before Linux has been started. For more
+details, see the
+.B lilo
+documentation.
+.LP
+Each partition has a type, its `Id', and if this type is 5 or f
+.IR "" "(`" "extended partition" "')"
+the starting sector of the partition
+again contains 4 partition descriptors. MSDOS only uses the
+first two of these: the first one an actual data partition,
+and the second one again an extended partition (or empty).
+In this way one gets a chain of extended partitions.
+Other operating systems have slightly different conventions.
+Linux also accepts type 85 as equivalent to 5 and f - this can be
+useful if one wants to have extended partitions under Linux past
+the 1024 cylinder boundary, without DOS FDISK hanging.
+(If there is no good reason, you should just use 5, which is
+understood by other systems.)
+.LP
+Partitions that are not primary or extended are called
+.IR logical .
+Often, one cannot boot from logical partitions (because the
+process of finding them is more involved than just looking
+at the MBR).
+Note that of an extended partition only the Id and the start
+are used. There are various conventions about what to write
+in the other fields. One should not try to use extended partitions
+for data storage or swap.
+
+.SH "INPUT FORMAT"
+.B sfdisk
+reads lines of the form
+.br
+.RS
+<start> <size> <id> <bootable> <c,h,s> <c,h,s>
+.RE
+where each line fills one partition descriptor.
+.LP
+Fields are separated by whitespace, or comma or semicolon possibly
+followed by whitespace; initial and trailing whitespace is ignored.
+Numbers can be octal, decimal or hexadecimal, decimal is default.
+When a field is absent or empty, a default value is used.
+.LP
+The <c,h,s> parts can (and probably should) be omitted -
+.B sfdisk
+computes them from <start> and <size> and the disk geometry
+as given by the kernel or specified using the \-H, \-S, \-C flags.
+.LP
+Bootable is specified as [*|\-], with as default not-bootable.
+(The value of this field is irrelevant for Linux - when Linux
+runs it has been booted already - but might play a role for
+certain boot loaders and for other operating systems.
+For example, when there are several primary DOS partitions,
+DOS assigns C: to the first among these that is bootable.)
+.LP
+Id is given in hex, without the 0x prefix, or is [E|S|L|X], where
+L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), E
+is EXTENDED_PARTITION (5), and X is LINUX_EXTENDED (85).
+.LP
+The default value of start is the first nonassigned sector/cylinder/...
+.LP
+The default value of size is as much as possible (until next
+partition or end-of-disk).
+.LP
+However, for the four partitions inside an extended partition,
+the defaults are: Linux partition, Extended partition, Empty, Empty.
+.LP
+But when the \-N option (change a single partition only) is given,
+the default for each field is its previous value.
+.LP
+A '+' can be specified instead of a number for size, which means
+as much as possible. This is useful with the \-N option.
+.SH EXAMPLE
+The command
+.RS
+.nf
+.if t .ft CW
+sfdisk /dev/hdc << EOF
+0,407
+,407
+;
+;
+EOF
+.if t .ft R
+.fi
+.RE
+will partition /dev/hdc just as indicated above.
+
+The command
+.RS
+.nf
+.if t .ft CW
+sfdisk /dev/hdb << EOF
+,3,L
+,60,L
+,19,S
+,,E
+,130,L
+,130,L
+,130,L
+,,L
+EOF
+.if t .ft R
+.fi
+.RE
+will partition /dev/hdb into two Linux partitions of 3 and 60
+cylinders, a swap space of 19 cylinders, and an extended partition
+covering the rest. Inside the extended partition there are four
+Linux logical partitions, three of 130 cylinders and one
+covering the rest.
+
+With the \-x option, the number of input lines must be a multiple of 4:
+you have to list the two empty partitions that you never want
+using two blank lines. Without the \-x option, you give one line
+for the partitions inside a extended partition, instead of four,
+and terminate with end-of-file (^D).
+(And
+.B sfdisk
+will assume that your input line represents the first of four,
+that the second one is extended, and the 3rd and 4th are empty.)
+.SH "CAUTION WARNINGS"
+
+The options marked with caution in the manual page are dangerous.
+For example not all functionality is completely implemented,
+which can be a reason for unexpected results.
+.SH "DOS 6.x WARNING"
+
+The DOS 6.x FORMAT command looks for some information in the first
+sector of the data area of the partition, and treats this information
+as more reliable than the information in the partition table. DOS
+FORMAT expects DOS FDISK to clear the first 512 bytes of the data area
+of a partition whenever a size change occurs. DOS FORMAT will look at
+this extra information even if the /U flag is given -- we consider
+this a bug in DOS FORMAT and DOS FDISK.
+.LP
+The bottom line is that if you use sfdisk to change the size of a
+DOS partition table entry, then you must also use
+.B dd
+to zero the first 512 bytes of that partition before using DOS FORMAT to
+format the partition. For example, if you were using sfdisk to make a DOS
+partition table entry for /dev/hda1, then (after exiting sfdisk and
+rebooting Linux so that the partition table information is valid) you
+would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero
+the first 512 bytes of the partition.
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+For best results, you should always use an OS-specific partition table
+program. For example, you should make DOS partitions with the DOS FDISK
+program and Linux partitions with the Linux sfdisk program.
+
+.SH "DRDOS WARNINGS"
+
+Stephen Tweedie reported (930515): `Most reports of superblock
+corruption turn out to be due to bad partitioning, with one filesystem
+overrunning the start of the next and corrupting its superblock.
+I have even had this problem with the supposedly-reliable DRDOS. This
+was quite possibly due to DRDOS-6.0's FDISK command. Unless I created
+a blank track or cylinder between the DRDOS partition and the
+immediately following one, DRDOS would happily stamp all over the
+start of the next partition. Mind you, as long as I keep a little
+free disk space after any DRDOS partition, I don't have any other
+problems with the two coexisting on the one drive.'
+
+A. V. Le Blanc writes in README.efdisk: `Dr. DOS 5.0 and 6.0 has been
+reported to have problems cooperating with Linux, and with this version
+of efdisk in particular. This efdisk sets the system type
+to hexadecimal 81. Dr. DOS seems to confuse
+this with hexadecimal 1, a DOS code. If you use Dr. DOS, use the
+efdisk command 't' to change the system code of any Linux partitions
+to some number less than hexadecimal 80; I suggest 41 and 42 for
+the moment.'
+
+A. V. Le Blanc writes in his README.fdisk: `DR-DOS 5.0 and 6.0
+are reported to have difficulties with partition ID codes of 80 or more.
+The Linux `fdisk' used to set the system type
+of new partitions to hexadecimal 81. DR-DOS seems to confuse this with
+hexadecimal 1, a DOS code. The values 82 for swap and 83 for file
+systems should not cause problems with DR-DOS. If they do, you may use
+the `fdisk' command `t' to change the system code of any Linux
+partitions to some number less than hexadecimal 80; I suggest 42 and 43
+for the moment.'
+
+In fact, it seems that only 4 bits are significant for the DRDOS FDISK,
+so that for example 11 and 21 are listed as DOS 2.0. However, DRDOS
+itself seems to use the full byte. I have not been able to reproduce
+any corruption with DRDOS or its fdisk.
+
+.SH BUGS
+There are too many options.
+.LP
+There is no support for non-DOS partition types.
+
+.\" .SH AUTHOR
+.\" A. E. Brouwer (aeb@cwi.nl)
+.\"
+.SH "SEE ALSO"
+.BR cfdisk (8),
+.BR fdisk (8),
+.BR mkfs (8),
+.BR parted (8),
+.BR partprobe (8),
+.BR kpartx (8)
+.SH AVAILABILITY
+The sfdisk command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/fdisks/sfdisk.c b/fdisks/sfdisk.c
new file mode 100644
index 0000000..15c6d33
--- /dev/null
+++ b/fdisks/sfdisk.c
@@ -0,0 +1,3242 @@
+/*
+ * sfdisk version 3.0 - aeb - 950813
+ *
+ * Copyright (C) 1995 Andries E. Brouwer (aeb@cwi.nl)
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation: either Version 1
+ * or (at your option) any later version.
+ *
+ * A.V. Le Blanc (LeBlanc@mcc.ac.uk) wrote Linux fdisk 1992-1994,
+ * patched by various people (faith@cs.unc.edu, martin@cs.unc.edu,
+ * leisner@sdsp.mc.xerox.com, esr@snark.thyrsus.com, aeb@cwi.nl)
+ * 1993-1995, with version numbers (as far as I have seen) 0.93 - 2.0e.
+ * This program had (head,sector,cylinder) as basic unit, and was
+ * (therefore) broken in several ways for the use on larger disks -
+ * for example, my last patch (from 2.0d to 2.0e) was required
+ * to allow a partition to cross cylinder 8064, and to write an
+ * extended partition past the 4GB mark.
+ *
+ * The current program is a rewrite from scratch, and I started a
+ * version numbering at 3.0.
+ * Andries Brouwer, aeb@cwi.nl, 950813
+ *
+ * Well, a good user interface is still lacking. On the other hand,
+ * many configurations cannot be handled by any other fdisk.
+ * I changed the name to sfdisk to prevent confusion. - aeb, 970501
+ */
+
+#define PROGNAME "sfdisk"
+
+#include <stdio.h>
+#include <stdlib.h> /* atoi, free */
+#include <stdarg.h> /* varargs */
+#include <unistd.h> /* read, write */
+#include <fcntl.h> /* O_RDWR */
+#include <errno.h> /* ERANGE */
+#include <string.h> /* strchr(), strrchr() */
+#include <ctype.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <limits.h>
+
+#include "c.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "blkdev.h"
+#include "linux_version.h"
+#include "common.h"
+#include "wholedisk.h"
+#include "gpt.h"
+#include "pathnames.h"
+#include "canonicalize.h"
+#include "rpmatch.h"
+#include "closestream.h"
+#include "strutils.h"
+
+/*
+ * Table of contents:
+ * A. About seeking
+ * B. About sectors
+ * C. About heads, sectors and cylinders
+ * D. About system Ids
+ * E. About partitions
+ * F. The standard input
+ * G. The command line
+ * H. Listing the current situation
+ * I. Writing the new situation
+ */
+int exit_status = 0;
+
+int force = 0; /* 1: do what I say, even if it is stupid ... */
+int quiet = 0; /* 1: suppress all warnings */
+/* IA-64 gcc spec file currently does -DLinux... */
+#undef Linux
+int Linux = 0; /* 1: suppress warnings irrelevant for Linux */
+int DOS = 0; /* 1: shift extended partitions by #sectors, not 1 */
+int DOS_extended = 0; /* 1: use starting cylinder boundary of extd partn */
+int dump = 0; /* 1: list in a format suitable for later input */
+int verify = 0; /* 1: check that listed partition is reasonable */
+int no_write = 0; /* 1: do not actually write to disk */
+int no_reread = 0; /* 1: skip the BLKRRPART ioctl test at startup */
+int leave_last = 0; /* 1: don't allocate the last cylinder */
+int opt_list = 0;
+char *save_sector_file = NULL;
+char *restore_sector_file = NULL;
+
+static void
+my_warn(char *s, ...) {
+ va_list p;
+
+ va_start(p, s);
+ if (!quiet) {
+ fflush(stdout);
+ vfprintf(stderr, s, p);
+ fflush(stderr);
+ }
+ va_end(p);
+}
+
+static void
+error(char *s, ...) {
+ va_list p;
+
+ va_start(p, s);
+ fflush(stdout);
+ fprintf(stderr, "\n" PROGNAME ": ");
+ vfprintf(stderr, s, p);
+ fflush(stderr);
+ va_end(p);
+}
+
+/*
+ * A. About seeking
+ */
+
+/*
+ * sseek: seek to specified sector - return 0 on failure
+ *
+ * Note: we use 512-byte sectors here, irrespective of the hardware ss.
+ */
+
+static int
+sseek(char *dev, int fd, unsigned long s) {
+ off_t in, out;
+ in = ((off_t) s << 9);
+
+ if ((out = lseek(fd, in, SEEK_SET)) != in) {
+ perror("lseek");
+ error(_("seek error on %s - cannot seek to %lu\n"), dev, s);
+ return 0;
+ }
+
+ if (in != out) {
+ error(_("seek error: wanted 0x%08x%08x, got 0x%08x%08x\n"),
+ (unsigned int)(in >> 32), (unsigned int)(in & 0xffffffff),
+ (unsigned int)(out >> 32), (unsigned int)(out & 0xffffffff));
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * B. About sectors
+ */
+
+/*
+ * We preserve all sectors read in a chain - some of these will
+ * have to be modified and written back.
+ */
+struct sector {
+ struct sector *next;
+ unsigned long long sectornumber;
+ int to_be_written;
+ char data[512];
+} *sectorhead;
+
+static void
+free_sectors(void) {
+ struct sector *s;
+
+ while (sectorhead) {
+ s = sectorhead;
+ sectorhead = s->next;
+ free(s);
+ }
+}
+
+static struct sector *
+get_sector(char *dev, int fd, unsigned long long sno) {
+ struct sector *s;
+
+ for (s = sectorhead; s; s = s->next)
+ if (s->sectornumber == sno)
+ return s;
+
+ if (!sseek(dev, fd, sno))
+ return 0;
+
+ s = xmalloc(sizeof(struct sector));
+
+ if (read(fd, s->data, sizeof(s->data)) != sizeof(s->data)) {
+ if (errno) /* 0 in case we read past end-of-disk */
+ perror("read");
+ error(_("read error on %s - cannot read sector %lu\n"), dev, sno);
+ free(s);
+ return 0;
+ }
+
+ s->next = sectorhead;
+ sectorhead = s;
+ s->sectornumber = sno;
+ s->to_be_written = 0;
+
+ return s;
+}
+
+static int
+msdos_signature(struct sector *s) {
+ unsigned char *data = (unsigned char *)s->data;
+ if (data[510] == 0x55 && data[511] == 0xaa)
+ return 1;
+ return 0;
+}
+
+static int
+write_sectors(char *dev, int fd) {
+ struct sector *s;
+
+ for (s = sectorhead; s; s = s->next)
+ if (s->to_be_written) {
+ if (!sseek(dev, fd, s->sectornumber))
+ return 0;
+ if (write(fd, s->data, sizeof(s->data)) != sizeof(s->data)) {
+ perror("write");
+ error(_("write error on %s - cannot write sector %lu\n"),
+ dev, s->sectornumber);
+ return 0;
+ }
+ s->to_be_written = 0;
+ }
+ return 1;
+}
+
+static void
+ulong_to_chars(unsigned long u, char *uu) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ uu[i] = (u & 0xff);
+ u >>= 8;
+ }
+}
+
+static unsigned long
+chars_to_ulong(unsigned char *uu) {
+ int i;
+ unsigned long u = 0;
+
+ for (i = 3; i >= 0; i--)
+ u = (u << 8) | uu[i];
+ return u;
+}
+
+static int
+save_sectors(char *dev, int fdin) {
+ struct sector *s;
+ char ss[516];
+ int fdout = -1;
+
+ fdout = open(save_sector_file, O_WRONLY | O_CREAT, 0444);
+ if (fdout < 0) {
+ perror(save_sector_file);
+ error(_("cannot open partition sector save file (%s)\n"),
+ save_sector_file);
+ goto err;
+ }
+
+ for (s = sectorhead; s; s = s->next)
+ if (s->to_be_written) {
+ ulong_to_chars(s->sectornumber, ss);
+ if (!sseek(dev, fdin, s->sectornumber))
+ goto err;
+ if (read(fdin, ss + 4, 512) != 512) {
+ perror("read");
+ error(_("read error on %s - cannot read sector %lu\n"),
+ dev, s->sectornumber);
+ goto err;
+ }
+ if (write(fdout, ss, sizeof(ss)) != sizeof(ss)) {
+ perror("write");
+ error(_("write error on %s\n"), save_sector_file);
+ goto err;
+ }
+ }
+
+ close(fdout);
+ return 1;
+
+ err:
+ if (fdout >= 0)
+ close(fdout);
+ return 0;
+}
+
+static int reread_disk_partition(char *dev, int fd);
+
+static int
+restore_sectors(char *dev) {
+ int fdin = -1, fdout = -1;
+ int ct;
+ struct stat statbuf;
+ char *ss0 = NULL, *ss;
+ unsigned long sno;
+
+ if (stat(restore_sector_file, &statbuf) < 0) {
+ perror(restore_sector_file);
+ error(_("cannot stat partition restore file (%s)\n"),
+ restore_sector_file);
+ goto err;
+ }
+ if (statbuf.st_size % 516) {
+ error(_("partition restore file has wrong size - not restoring\n"));
+ goto err;
+ }
+
+ ss0 = xmalloc(statbuf.st_size);
+ ss = ss0;
+
+ fdin = open(restore_sector_file, O_RDONLY);
+ if (fdin < 0) {
+ perror(restore_sector_file);
+ error(_("cannot open partition restore file (%s)\n"),
+ restore_sector_file);
+ goto err;
+ }
+ if (read(fdin, ss, statbuf.st_size) != statbuf.st_size) {
+ perror("read");
+ error(_("error reading %s\n"), restore_sector_file);
+ goto err;
+ }
+
+ fdout = open(dev, O_WRONLY);
+ if (fdout < 0) {
+ perror(dev);
+ error(_("cannot open device %s for writing\n"), dev);
+ goto err;
+ }
+
+ ct = statbuf.st_size / 516;
+ while (ct--) {
+ sno = chars_to_ulong((unsigned char *)ss);
+ if (!sseek(dev, fdout, sno))
+ goto err;
+ if (write(fdout, ss + 4, 512) != 512) {
+ perror(dev);
+ error(_("error writing sector %lu on %s\n"), sno, dev);
+ goto err;
+ }
+ ss += 516;
+ }
+ free(ss0);
+ ss0 = NULL;
+
+ if (!reread_disk_partition(dev, fdout)) /* closes fdout */
+ goto err;
+ close(fdin);
+
+ return 1;
+
+ err:
+ free(ss0);
+ if (fdin >= 0)
+ close(fdin);
+ if (fdout >= 0)
+ close(fdout);
+
+ return 0;
+}
+
+/*
+ * C. About heads, sectors and cylinders
+ */
+
+/*
+ * <linux/hdreg.h> defines HDIO_GETGEO and
+ * struct hd_geometry {
+ * unsigned char heads;
+ * unsigned char sectors;
+ * unsigned short cylinders;
+ * unsigned long start;
+ * };
+ *
+ * For large disks g.cylinders is truncated, so we use BLKGETSIZE.
+ */
+
+/*
+ * We consider several geometries for a disk:
+ * B - the BIOS geometry, gotten from the kernel via HDIO_GETGEO
+ * F - the fdisk geometry
+ * U - the user-specified geometry
+ *
+ * 0 means unspecified / unknown
+ */
+struct geometry {
+ unsigned long long total_size; /* in sectors */
+ unsigned long cylindersize; /* in sectors */
+ unsigned long heads, sectors, cylinders;
+ unsigned long start;
+} B, F, U;
+
+static struct geometry
+get_geometry(char *dev, int fd, int silent) {
+ struct hd_geometry g;
+ unsigned long cyls;
+ unsigned long long sectors;
+ struct geometry R;
+
+#ifdef HDIO_GETGEO
+ if (ioctl(fd, HDIO_GETGEO, &g))
+#endif
+ {
+ g.heads = g.sectors = g.cylinders = g.start = 0;
+ if (!silent)
+ warnx(_("Disk %s: cannot get geometry\n"), dev);
+ }
+
+ R.start = g.start;
+ R.heads = g.heads;
+ R.sectors = g.sectors;
+ R.cylindersize = R.heads * R.sectors;
+ R.cylinders = 0;
+ R.total_size = 0;
+
+ if (blkdev_get_sectors(fd, &sectors) == -1) {
+ /* maybe an ordinary file */
+ struct stat s;
+
+ if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode))
+ R.total_size = (s.st_size >> 9);
+ else if (!silent)
+ warnx(_("Disk %s: cannot get size\n"), dev);
+ } else
+ R.total_size = sectors;
+
+ if (R.cylindersize && R.total_size) {
+ sectors /= R.cylindersize;
+ cyls = sectors;
+ if (cyls != sectors)
+ cyls = ~0;
+ R.cylinders = cyls;
+ }
+
+ return R;
+}
+
+static void
+get_cylindersize(char *dev, int fd, int silent) {
+ struct geometry R;
+
+ R = get_geometry(dev, fd, silent);
+
+ B.heads = (U.heads ? U.heads : R.heads ? R.heads : 255);
+ B.sectors = (U.sectors ? U.sectors : R.sectors ? R.sectors : 63);
+ B.cylinders = (U.cylinders ? U.cylinders : R.cylinders);
+
+ B.cylindersize = B.heads * B.sectors;
+ B.total_size = R.total_size;
+
+ if (B.cylinders == 0 && B.cylindersize != 0)
+ B.cylinders = B.total_size / B.cylindersize;
+
+ if (R.start && !force) {
+ my_warn(_("Warning: start=%lu - this looks like a partition rather than\n"
+ "the entire disk. Using fdisk on it is probably meaningless.\n"
+ "[Use the --force option if you really want this]\n"),
+ R.start);
+ exit(1);
+ }
+#if 0
+ if (R.heads && B.heads != R.heads)
+ my_warn(_("Warning: HDIO_GETGEO says that there are %lu heads\n"),
+ R.heads);
+ if (R.sectors && B.sectors != R.sectors)
+ my_warn(_("Warning: HDIO_GETGEO says that there are %lu sectors\n"),
+ R.sectors);
+ if (R.cylinders && B.cylinders != R.cylinders
+ && B.cylinders < 65536 && R.cylinders < 65536)
+ my_warn(_("Warning: BLKGETSIZE/HDIO_GETGEO says that there are %lu cylinders\n"),
+ R.cylinders);
+#endif
+
+ if (B.sectors > 63)
+ my_warn(_("Warning: unlikely number of sectors (%lu) - usually at most 63\n"
+ "This will give problems with all software that uses C/H/S addressing.\n"),
+ B.sectors);
+ if (!silent)
+ printf(_("\nDisk %s: %lu cylinders, %lu heads, %lu sectors/track\n"),
+ dev, B.cylinders, B.heads, B.sectors);
+}
+
+typedef struct {
+ unsigned char h, s, c;
+} __attribute__ ((packed)) chs; /* has some c bits in s */
+chs zero_chs = { 0, 0, 0 };
+
+typedef struct {
+ unsigned long h, s, c;
+} longchs;
+longchs zero_longchs;
+
+static chs
+longchs_to_chs(longchs aa, struct geometry G) {
+ chs a;
+
+ if (aa.h < 256 && aa.s < 64 && aa.c < 1024) {
+ a.h = aa.h;
+ a.s = aa.s | ((aa.c >> 2) & 0xc0);
+ a.c = (aa.c & 0xff);
+ } else if (G.heads && G.sectors) {
+ a.h = G.heads - 1;
+ a.s = G.sectors | 0xc0;
+ a.c = 0xff;
+ } else
+ a = zero_chs;
+ return a;
+}
+
+static longchs
+chs_to_longchs(chs a) {
+ longchs aa;
+
+ aa.h = a.h;
+ aa.s = (a.s & 0x3f);
+ aa.c = (a.s & 0xc0);
+ aa.c = (aa.c << 2) + a.c;
+ return aa;
+}
+
+static longchs
+ulong_to_longchs(unsigned long sno, struct geometry G) {
+ longchs aa;
+
+ if (G.heads && G.sectors && G.cylindersize) {
+ aa.s = 1 + sno % G.sectors;
+ aa.h = (sno / G.sectors) % G.heads;
+ aa.c = sno / G.cylindersize;
+ return aa;
+ } else {
+ return zero_longchs;
+ }
+}
+
+static chs
+ulong_to_chs(unsigned long sno, struct geometry G) {
+ return longchs_to_chs(ulong_to_longchs(sno, G), G);
+}
+
+#if 0
+static unsigned long
+longchs_to_ulong(longchs aa, struct geometry G) {
+ return (aa.c * G.cylindersize + aa.h * G.sectors + aa.s - 1);
+}
+
+static unsigned long
+chs_to_ulong(chs a, struct geometry G) {
+ return longchs_to_ulong(chs_to_longchs(a), G);
+}
+#endif
+
+static int
+is_equal_chs(chs a, chs b) {
+ return (a.h == b.h && a.s == b.s && a.c == b.c);
+}
+
+static int
+chs_ok(chs a, char *v, char *w) {
+ longchs aa = chs_to_longchs(a);
+ int ret = 1;
+
+ if (is_equal_chs(a, zero_chs))
+ return 1;
+ if (B.heads && aa.h >= B.heads) {
+ my_warn(_("%s of partition %s has impossible value for head: "
+ "%lu (should be in 0-%lu)\n"), w, v, aa.h, B.heads - 1);
+ ret = 0;
+ }
+ if (B.sectors && (aa.s == 0 || aa.s > B.sectors)) {
+ my_warn(_("%s of partition %s has impossible value for sector: "
+ "%lu (should be in 1-%lu)\n"), w, v, aa.s, B.sectors);
+ ret = 0;
+ }
+ if (B.cylinders && aa.c >= B.cylinders) {
+ my_warn(_("%s of partition %s has impossible value for cylinders: "
+ "%lu (should be in 0-%lu)\n"), w, v, aa.c, B.cylinders - 1);
+ ret = 0;
+ }
+ return ret;
+}
+
+/*
+ * D. About system Ids
+ */
+
+#define EMPTY_PARTITION 0
+#define EXTENDED_PARTITION 5
+#define WIN98_EXTENDED 0x0f
+#define DM6_AUX1PARTITION 0x51
+#define DM6_AUX3PARTITION 0x53
+#define DM6_PARTITION 0x54
+#define EZD_PARTITION 0x55
+#define LINUX_SWAP 0x82
+#define LINUX_NATIVE 0x83
+#define LINUX_EXTENDED 0x85
+#define BSD_PARTITION 0xa5
+#define NETBSD_PARTITION 0xa9
+
+/* List of partition types now in i386_sys_types.c */
+
+static const char *
+sysname(unsigned char type) {
+ struct systypes *s;
+
+ for (s = i386_sys_types; s->name; s++)
+ if (s->type == type)
+ return _(s->name);
+ return _("Unknown");
+}
+
+static void
+list_types(void) {
+ struct systypes *s;
+
+ printf(_("Id Name\n\n"));
+ for (s = i386_sys_types; s->name; s++)
+ printf("%2x %s\n", s->type, _(s->name));
+}
+
+static int
+is_extended(unsigned char type) {
+ return (type == EXTENDED_PARTITION
+ || type == LINUX_EXTENDED || type == WIN98_EXTENDED);
+}
+
+static int
+is_bsd(unsigned char type) {
+ return (type == BSD_PARTITION || type == NETBSD_PARTITION);
+}
+
+/*
+ * E. About partitions
+ */
+
+/* MS/DOS partition */
+
+struct partition {
+ unsigned char bootable; /* 0 or 0x80 */
+ chs begin_chs;
+ unsigned char sys_type;
+ chs end_chs;
+ unsigned int start_sect; /* starting sector counting from 0 */
+ unsigned int nr_sects; /* nr of sectors in partition */
+} __attribute__ ((packed));
+
+/* Unfortunately, partitions are not aligned, and non-Intel machines
+ are unhappy with non-aligned integers. So, we need a copy by hand. */
+static int
+copy_to_int(unsigned char *cp) {
+ unsigned int m;
+
+ m = *cp++;
+ m += (*cp++ << 8);
+ m += (*cp++ << 16);
+ m += (*cp++ << 24);
+ return m;
+}
+
+static void
+copy_from_int(int m, char *cp) {
+ *cp++ = (m & 0xff);
+ m >>= 8;
+ *cp++ = (m & 0xff);
+ m >>= 8;
+ *cp++ = (m & 0xff);
+ m >>= 8;
+ *cp++ = (m & 0xff);
+}
+
+static void
+copy_to_part(char *cp, struct partition *p) {
+ p->bootable = *cp++;
+ p->begin_chs.h = *cp++;
+ p->begin_chs.s = *cp++;
+ p->begin_chs.c = *cp++;
+ p->sys_type = *cp++;
+ p->end_chs.h = *cp++;
+ p->end_chs.s = *cp++;
+ p->end_chs.c = *cp++;
+ p->start_sect = copy_to_int((unsigned char *)cp);
+ p->nr_sects = copy_to_int((unsigned char *)cp + 4);
+}
+
+static void
+copy_from_part(struct partition *p, char *cp) {
+ *cp++ = p->bootable;
+ *cp++ = p->begin_chs.h;
+ *cp++ = p->begin_chs.s;
+ *cp++ = p->begin_chs.c;
+ *cp++ = p->sys_type;
+ *cp++ = p->end_chs.h;
+ *cp++ = p->end_chs.s;
+ *cp++ = p->end_chs.c;
+ copy_from_int(p->start_sect, cp);
+ copy_from_int(p->nr_sects, cp + 4);
+}
+
+/* Roughly speaking, Linux doesn't use any of the above fields except
+ for partition type, start sector and number of sectors. (However,
+ see also linux/drivers/scsi/fdomain.c.)
+ The only way partition type is used (in the kernel) is the comparison
+ for equality with EXTENDED_PARTITION (and these Disk Manager types). */
+
+struct part_desc {
+ unsigned long long start;
+ unsigned long long size;
+ unsigned long long sector, offset; /* disk location of this info */
+ struct partition p;
+ struct part_desc *ep; /* extended partition containing this one */
+ int ptype;
+#define DOS_TYPE 0
+#define BSD_TYPE 1
+} zero_part_desc;
+
+static struct part_desc *
+outer_extended_partition(struct part_desc *p) {
+ while (p->ep)
+ p = p->ep;
+ return p;
+}
+
+static int
+is_parent(struct part_desc *pp, struct part_desc *p) {
+ while (p) {
+ if (pp == p)
+ return 1;
+ p = p->ep;
+ }
+ return 0;
+}
+
+struct disk_desc {
+ struct part_desc partitions[512];
+ int partno;
+} oldp, newp;
+
+/* determine where on the disk this information goes */
+static void
+add_sector_and_offset(struct disk_desc *z) {
+ int pno;
+ struct part_desc *p;
+
+ for (pno = 0; pno < z->partno; pno++) {
+ p = &(z->partitions[pno]);
+ p->offset = 0x1be + (pno % 4) * sizeof(struct partition);
+ p->sector = (p->ep ? p->ep->start : 0);
+ }
+}
+
+/* tell the kernel to reread the partition tables */
+static int
+reread_ioctl(int fd) {
+#ifdef BLKRRPART
+ if (ioctl(fd, BLKRRPART))
+#else
+ errno = ENOSYS;
+#endif
+ {
+ /* perror might change errno */
+ int err = errno;
+
+ perror("BLKRRPART");
+
+ /* 2.6.8 returns EIO for a zero table */
+ if (err == EBUSY)
+ return -1;
+ }
+ return 0;
+}
+
+/* reread after writing */
+static int
+reread_disk_partition(char *dev, int fd) {
+ printf(_("Re-reading the partition table ...\n"));
+ fflush(stdout);
+ sync();
+
+ if (reread_ioctl(fd) && is_blkdev(fd)) {
+ warnx(_("The command to re-read the partition table failed.\n"
+ "Run partprobe(8), kpartx(8) or reboot your system now,\n"
+ "before using mkfs\n"));
+ return 0;
+ }
+
+ if (fsync(fd) || close(fd)) {
+ perror(dev);
+ warnx(_("Error closing %s\n"), dev);
+ return 0;
+ }
+ printf("\n");
+
+ return 1;
+}
+
+/* find Linux name of this partition, assuming that it will have a name */
+static int
+index_to_linux(int pno, struct disk_desc *z) {
+ int i, ct = 1;
+ struct part_desc *p = &(z->partitions[0]);
+ for (i = 0; i < pno; i++, p++)
+ if (i < 4 || (p->size > 0 && !is_extended(p->p.sys_type)))
+ ct++;
+ return ct;
+}
+
+static int
+linux_to_index(int lpno, struct disk_desc *z) {
+ int i, ct = 0;
+ struct part_desc *p = &(z->partitions[0]);
+ for (i = 0; i < z->partno && ct < lpno; i++, p++)
+ if ((i < 4 || (p->size > 0 && !is_extended(p->p.sys_type)))
+ && ++ct == lpno)
+ return i;
+ return -1;
+}
+
+static int
+asc_to_index(char *pnam, struct disk_desc *z) {
+ int pnum, pno;
+
+ if (*pnam == '#') {
+ pno = atoi(pnam + 1);
+ } else {
+ pnum = atoi(pnam);
+ pno = linux_to_index(pnum, z);
+ }
+ if (!(pno >= 0 && pno < z->partno))
+ errx(EXIT_FAILURE, _("%s: no such partition\n"), pnam);
+ return pno;
+}
+
+/*
+ * List partitions - in terms of sectors, blocks or cylinders
+ */
+#define F_SECTOR 1
+#define F_BLOCK 2
+#define F_CYLINDER 3
+#define F_MEGABYTE 4
+
+int default_format = F_MEGABYTE;
+int specified_format = 0;
+int show_extended = 0;
+int one_only = 0;
+int one_only_pno;
+int increment = 0;
+
+static void
+set_format(char c) {
+ switch (c) {
+ default:
+ warnx(_("unrecognized format - using sectors\n"));
+ /* fallthrough */
+ case 'S':
+ specified_format = F_SECTOR;
+ break;
+ case 'B':
+ specified_format = F_BLOCK;
+ break;
+ case 'C':
+ specified_format = F_CYLINDER;
+ break;
+ case 'M':
+ specified_format = F_MEGABYTE;
+ break;
+ }
+}
+
+static unsigned long
+unitsize(int format) {
+ default_format = (B.cylindersize ? F_CYLINDER : F_MEGABYTE);
+ if (!format && !(format = specified_format))
+ format = default_format;
+
+ switch (format) {
+ default:
+ case F_CYLINDER:
+ if (B.cylindersize)
+ return B.cylindersize;
+ case F_SECTOR:
+ return 1;
+ case F_BLOCK:
+ return 2;
+ case F_MEGABYTE:
+ return 2048;
+ }
+}
+
+static unsigned long long
+get_disksize(int format) {
+ if (B.total_size && leave_last)
+ /* don't use last cylinder (--leave-last option) */
+ return (B.total_size - B.cylindersize) / unitsize(format);
+
+ return B.total_size / unitsize(format);
+}
+
+static void
+out_partition_header(char *dev, int format, struct geometry G) {
+ if (dump) {
+ printf("# partition table of %s\n", dev);
+ printf("unit: sectors\n\n");
+ return;
+ }
+
+ default_format = (G.cylindersize ? F_CYLINDER : F_MEGABYTE);
+ if (!format && !(format = specified_format))
+ format = default_format;
+
+ switch (format) {
+ default:
+ warnx(_("unimplemented format - using %s\n"),
+ G.cylindersize ? _("cylinders") : _("sectors"));
+ case F_CYLINDER:
+ if (G.cylindersize) {
+ printf(_("Units: cylinders of %lu bytes, blocks of 1024 bytes"
+ ", counting from %d\n\n"), G.cylindersize << 9, increment);
+ printf(_(" Device Boot Start End #cyls #blocks Id System\n"));
+ break;
+ }
+ /* fall through */
+ case F_SECTOR:
+ printf(_("Units: sectors of 512 bytes, counting from %d\n\n"),
+ increment);
+ printf(_(" Device Boot Start End #sectors Id System\n"));
+ break;
+ case F_BLOCK:
+ printf(_("Units: blocks of 1024 bytes, counting from %d\n\n"),
+ increment);
+ printf(_(" Device Boot Start End #blocks Id System\n"));
+ break;
+ case F_MEGABYTE:
+ printf(_("Units: 1MiB = 1024*1024 bytes, blocks of 1024 bytes"
+ ", counting from %d\n\n"), increment);
+ printf(_(" Device Boot Start End MiB #blocks Id System\n"));
+ break;
+ }
+}
+
+static void
+out_rounddown(int width, unsigned long long n, unsigned long unit, int inc) {
+ printf("%*llu", width, inc + n / unit);
+ if (unit != 1)
+ putchar((n % unit) ? '+' : ' ');
+ putchar(' ');
+}
+
+static void
+out_roundup(int width, unsigned long long n, unsigned long unit, int inc) {
+ if (n == (unsigned long long)(-1))
+ printf("%*s", width, "-");
+ else
+ printf("%*llu", width, inc + n / unit);
+ if (unit != 1)
+ putchar(((n + 1) % unit) ? '-' : ' ');
+ putchar(' ');
+}
+
+static void
+out_roundup_size(int width, unsigned long long n, unsigned long unit) {
+ printf("%*llu", width, (n + unit - 1) / unit);
+ if (unit != 1)
+ putchar((n % unit) ? '-' : ' ');
+ putchar(' ');
+}
+
+static struct geometry
+get_fdisk_geometry_one(struct part_desc *p) {
+ struct geometry G;
+
+ memset(&G, 0, sizeof(struct geometry));
+ chs b = p->p.end_chs;
+ longchs bb = chs_to_longchs(b);
+ G.heads = bb.h + 1;
+ G.sectors = bb.s;
+ G.cylindersize = G.heads * G.sectors;
+ return G;
+}
+
+static int
+get_fdisk_geometry(struct disk_desc *z) {
+ struct part_desc *p;
+ int pno, agree;
+ struct geometry G0, G;
+
+ memset(&G0, 0, sizeof(struct geometry));
+ agree = 0;
+ for (pno = 0; pno < z->partno; pno++) {
+ p = &(z->partitions[pno]);
+ if (p->size != 0 && p->p.sys_type != 0) {
+ G = get_fdisk_geometry_one(p);
+ if (!G0.heads) {
+ G0 = G;
+ agree = 1;
+ } else if (G.heads != G0.heads || G.sectors != G0.sectors) {
+ agree = 0;
+ break;
+ }
+ }
+ }
+ F = (agree ? G0 : B);
+ return (F.sectors != B.sectors || F.heads != B.heads);
+}
+
+static void
+out_partition(char *dev, int format, struct part_desc *p,
+ struct disk_desc *z, struct geometry G) {
+ unsigned long long start, end, size;
+ int pno, lpno;
+
+ if (!format && !(format = specified_format))
+ format = default_format;
+
+ pno = p - &(z->partitions[0]); /* our index */
+ lpno = index_to_linux(pno, z); /* name of next one that has a name */
+ if (pno == linux_to_index(lpno, z)) /* was that us? */
+ printf("%s", partname(dev, lpno, 10)); /* yes */
+ else if (show_extended)
+ printf(" - ");
+ else
+ return;
+ putchar(dump ? ':' : ' ');
+
+ start = p->start;
+ end = p->start + p->size - 1;
+ size = p->size;
+
+ if (dump) {
+ printf(" start=%9llu", start);
+ printf(", size=%9llu", size);
+ if (p->ptype == DOS_TYPE) {
+ printf(", Id=%2x", p->p.sys_type);
+ if (p->p.bootable == 0x80)
+ printf(", bootable");
+ }
+ printf("\n");
+ return;
+ }
+
+ if (p->ptype != DOS_TYPE || p->p.bootable == 0)
+ printf(" ");
+ else if (p->p.bootable == 0x80)
+ printf(" * ");
+ else
+ printf(" ? "); /* garbage */
+
+ switch (format) {
+ case F_CYLINDER:
+ if (G.cylindersize) {
+ out_rounddown(6, start, G.cylindersize, increment);
+ out_roundup(6, end, G.cylindersize, increment);
+ out_roundup_size(6, size, G.cylindersize);
+ out_rounddown(9, size, 2, 0);
+ break;
+ }
+ /* fall through */
+ default:
+ case F_SECTOR:
+ out_rounddown(9, start, 1, increment);
+ out_roundup(9, end, 1, increment);
+ out_rounddown(10, size, 1, 0);
+ break;
+ case F_BLOCK:
+#if 0
+ printf("%8lu,%3lu ",
+ p->sector / 2, ((p->sector & 1) ? 512 : 0) + p->offset);
+#endif
+ out_rounddown(8, start, 2, increment);
+ out_roundup(8, end, 2, increment);
+ out_rounddown(9, size, 2, 0);
+ break;
+ case F_MEGABYTE:
+ out_rounddown(5, start, 2048, increment);
+ out_roundup(5, end, 2048, increment);
+ out_roundup_size(5, size, 2048);
+ out_rounddown(9, size, 2, 0);
+ break;
+ }
+ if (p->ptype == DOS_TYPE) {
+ printf(" %2x %s\n", p->p.sys_type, sysname(p->p.sys_type));
+ } else {
+ printf("\n");
+ }
+
+ /* Is chs as we expect? */
+ if (!quiet && p->ptype == DOS_TYPE) {
+ chs a, b;
+ longchs aa, bb;
+ a = (size ? ulong_to_chs(start, G) : zero_chs);
+ b = p->p.begin_chs;
+ aa = chs_to_longchs(a);
+ bb = chs_to_longchs(b);
+ if (a.s && !is_equal_chs(a, b))
+ warnx(_("\t\tstart: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
+ aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
+ a = (size ? ulong_to_chs(end, G) : zero_chs);
+ b = p->p.end_chs;
+ aa = chs_to_longchs(a);
+ bb = chs_to_longchs(b);
+ if (a.s && !is_equal_chs(a, b))
+ warnx(_("\t\tend: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
+ aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
+ if (G.cylinders && G.cylinders < 1024 && bb.c > G.cylinders)
+ warnx(_("partition ends on cylinder %ld, beyond the end of the disk\n"),
+ bb.c);
+ }
+}
+
+static void
+out_partitions(char *dev, struct disk_desc *z) {
+ int pno, format = 0;
+
+ if (z->partno == 0) {
+ if (!opt_list)
+ warnx(_("No partitions found\n"));
+ } else {
+ if (get_fdisk_geometry(z) && !dump) {
+ warnx(_("Warning: The partition table looks like it was made\n"
+ " for C/H/S=*/%ld/%ld (instead of %ld/%ld/%ld).\n"
+ "For this listing I'll assume that geometry.\n"),
+ F.heads, F.sectors, B.cylinders, B.heads, B.sectors);
+ }
+
+ out_partition_header(dev, format, F);
+ for (pno = 0; pno < z->partno; pno++) {
+ out_partition(dev, format, &(z->partitions[pno]), z, F);
+ if (show_extended && pno % 4 == 3)
+ printf("\n");
+ }
+ }
+}
+
+static int
+disj(struct part_desc *p, struct part_desc *q) {
+ return ((p->start + p->size <= q->start)
+ || (is_extended(p->p.sys_type)
+ && q->start + q->size <= p->start + p->size));
+}
+
+static char *
+pnumber(struct part_desc *p, struct disk_desc *z) {
+ static char buf[20];
+ int this, next;
+ struct part_desc *p0 = &(z->partitions[0]);
+
+ this = index_to_linux(p - p0, z);
+ next = index_to_linux(p - p0 + 1, z);
+
+ if (next > this)
+ sprintf(buf, "%d", this);
+ else
+ sprintf(buf, "[%d]", this);
+ return buf;
+}
+
+static int
+partitions_ok(int fd, struct disk_desc *z) {
+ struct part_desc *partitions = &(z->partitions[0]), *p, *q;
+ int partno = z->partno;
+
+#define PNO(p) pnumber(p, z)
+
+ /* Have at least 4 partitions been defined? */
+ if (partno < 4) {
+ if (!partno)
+ errx(EXIT_FAILURE, _("no partition table present."));
+ else
+ errx(EXIT_FAILURE, _("strange, only %d partitions defined."), partno);
+ return 0;
+ }
+
+ /* Are the partitions of size 0 marked empty?
+ And do they have start = 0? And bootable = 0? */
+ for (p = partitions; p - partitions < partno; p++)
+ if (p->size == 0) {
+ if (p->p.sys_type != EMPTY_PARTITION)
+ my_warn(_("Warning: partition %s has size 0 but is not marked Empty\n"),
+ PNO(p));
+ else if (p->p.bootable != 0)
+ my_warn(_("Warning: partition %s has size 0 and is bootable\n"),
+ PNO(p));
+ else if (p->p.start_sect != 0)
+ my_warn(_("Warning: partition %s has size 0 and nonzero start\n"),
+ PNO(p));
+ /* all this is probably harmless, no error return */
+ }
+
+ /* Are the logical partitions contained in their extended partitions? */
+ for (p = partitions + 4; p < partitions + partno; p++)
+ if (p->ptype == DOS_TYPE)
+ if (p->size && !is_extended(p->p.sys_type)) {
+ q = p->ep;
+ if (p->start < q->start
+ || p->start + p->size > q->start + q->size) {
+ my_warn(_("Warning: partition %s is not contained in "
+ "partition %s\n"), PNO(p), PNO(q));
+ return 0;
+ }
+ }
+
+ /* Are the data partitions mutually disjoint? */
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->size && !is_extended(p->p.sys_type))
+ for (q = p + 1; q < partitions + partno; q++)
+ if (q->size && !is_extended(q->p.sys_type))
+ if (!((p->start > q->start) ? disj(q, p) : disj(p, q))) {
+ my_warn(_("Warning: partitions %s and %s overlap\n"),
+ PNO(p), PNO(q));
+ return 0;
+ }
+
+ /* Are the data partitions and the extended partition
+ table sectors disjoint? */
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->size && !is_extended(p->p.sys_type))
+ for (q = partitions; q < partitions + partno; q++)
+ if (is_extended(q->p.sys_type))
+ if (p->start <= q->start && p->start + p->size > q->start) {
+ my_warn(_("Warning: partition %s contains part of "
+ "the partition table (sector %llu),\n"
+ "and will destroy it when filled\n"),
+ PNO(p), q->start);
+ return 0;
+ }
+
+ /* Do they start past zero and end before end-of-disk? */
+ {
+ unsigned long long ds = get_disksize(F_SECTOR);
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->size) {
+ if (p->start == 0) {
+ my_warn(_("Warning: partition %s starts at sector 0\n"),
+ PNO(p));
+ return 0;
+ }
+ if (p->size && p->start + p->size > ds) {
+ my_warn(_("Warning: partition %s extends past end of disk\n"),
+ PNO(p));
+ return 0;
+ }
+ }
+ }
+
+ int sector_size;
+ if (blkdev_get_sector_size(fd, &sector_size) == -1)
+ sector_size = DEFAULT_SECTOR_SIZE;
+
+ /* Is the size of partitions less than 2^32 sectors limit? */
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->size > UINT_MAX) {
+ unsigned long long bytes = p->size * sector_size;
+ int giga = bytes / 1000000000;
+ int hectogiga = (giga + 50) / 100;
+ my_warn(_("Warning: partition %s has size %d.%d TB (%llu bytes),\n"
+ "which is larger than the %llu bytes limit imposed\n"
+ "by the DOS partition table for %d-byte sectors\n"),
+ PNO(p), hectogiga / 10, hectogiga % 10,
+ bytes,
+ (unsigned long long) UINT_MAX * sector_size,
+ sector_size);
+ return 0;
+ }
+
+ /* Do the partitions start below the 2^32 sectors limit? */
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->start > UINT_MAX) {
+ unsigned long long bytes = p->start * sector_size;
+ int giga = bytes / 1000000000;
+ int hectogiga = (giga + 50) / 100;
+ my_warn(_("Warning: partition %s starts at sector %llu (%d.%d TB for %d-byte sectors),\n"
+ "which exceeds the DOS partition table limit of %llu sectors\n"),
+ PNO(p),
+ p->start,
+ hectogiga / 10,
+ hectogiga % 10,
+ sector_size,
+ (unsigned long long) UINT_MAX);
+ return 0;
+ }
+
+ /* At most one chain of DOS extended partitions ? */
+ /* It seems that the OS/2 fdisk has the additional requirement
+ that the extended partition must be the fourth one */
+ {
+ int ect = 0;
+ for (p = partitions; p < partitions + 4; p++)
+ if (p->p.sys_type == EXTENDED_PARTITION)
+ ect++;
+ if (ect > 1 && !Linux) {
+ my_warn(_("Among the primary partitions, at most one can be extended\n"
+ " (although this is not a problem under Linux)\n"));
+ return 0;
+ }
+ }
+
+ /*
+ * Do all partitions start at a cylinder boundary ?
+ * (this is not required for Linux)
+ * The first partition starts after MBR.
+ * Logical partitions start slightly after the containing extended partn.
+ */
+ if (B.cylindersize && !Linux) {
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->size) {
+ if (p->start % B.cylindersize != 0
+ && (!p->ep
+ || p->start / B.cylindersize !=
+ p->ep->start / B.cylindersize)
+ && (p->p.start_sect >= B.cylindersize)) {
+ my_warn(_("Warning: partition %s does not start "
+ "at a cylinder boundary\n"), PNO(p));
+ if (specified_format == F_CYLINDER)
+ return 0;
+ }
+ if ((p->start + p->size) % B.cylindersize) {
+ my_warn(_("Warning: partition %s does not end "
+ "at a cylinder boundary\n"), PNO(p));
+ if (specified_format == F_CYLINDER)
+ return 0;
+ }
+ }
+ }
+
+ /* Usually, one can boot only from primary partitions. */
+ /* In fact, from a unique one only. */
+ /* do not warn about bootable extended partitions -
+ often LILO is there */
+ {
+ int pno = -1;
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->p.bootable) {
+ if (pno == -1)
+ pno = p - partitions;
+ else if (p - partitions < 4) {
+ my_warn(_("Warning: more than one primary partition is marked "
+ "bootable (active)\n"
+ "This does not matter for LILO, but the DOS MBR will "
+ "not boot this disk.\n"));
+ break;
+ }
+ if (p - partitions >= 4) {
+ my_warn(_("Warning: usually one can boot from primary partitions "
+ "only\nLILO disregards the `bootable' flag.\n"));
+ break;
+ }
+ }
+ if (pno == -1 || pno >= 4)
+ my_warn(_("Warning: no primary partition is marked bootable (active)\n"
+ "This does not matter for LILO, but the DOS MBR will "
+ "not boot this disk.\n"));
+ }
+
+ /* Is chs as we expect? */
+ for (p = partitions; p < partitions + partno; p++)
+ if (p->ptype == DOS_TYPE) {
+ chs a, b;
+ longchs aa, bb;
+ a = p->size ? ulong_to_chs(p->start, B) : zero_chs;
+ b = p->p.begin_chs;
+ aa = chs_to_longchs(a);
+ bb = chs_to_longchs(b);
+ if (!Linux && !chs_ok(b, PNO(p), _("start")))
+ return 0;
+ if (a.s && !is_equal_chs(a, b))
+ my_warn(_("partition %s: start: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
+ PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
+ a = p->size ? ulong_to_chs(p->start + p->size - 1, B) : zero_chs;
+ b = p->p.end_chs;
+ aa = chs_to_longchs(a);
+ bb = chs_to_longchs(b);
+ if (!Linux && !chs_ok(b, PNO(p), _("end")))
+ return 0;
+ if (a.s && !is_equal_chs(a, b))
+ my_warn(_("partition %s: end: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"),
+ PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s);
+ if (B.cylinders && B.cylinders < 1024 && bb.c > B.cylinders)
+ my_warn(_("partition %s ends on cylinder %ld, beyond the end of the disk\n"),
+ PNO(p), bb.c);
+ }
+
+ return 1;
+
+#undef PNO
+}
+
+static void
+extended_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) {
+ char *cp;
+ struct sector *s;
+ unsigned long long start, here, next;
+ int i, moretodo = 1;
+ struct partition p;
+ struct part_desc *partitions = &(z->partitions[0]);
+ size_t pno = z->partno;
+
+ here = start = ep->start;
+
+ if (B.cylindersize && start % B.cylindersize) {
+ /* This is BAD */
+ if (DOS_extended) {
+ here = start -= (start % B.cylindersize);
+ warnx(_("Warning: shifted start of the extd partition "
+ "from %lld to %lld\n"
+ "(For listing purposes only. "
+ "Do not change its contents.)\n"), ep->start, start);
+ } else {
+ warnx(_("Warning: extended partition does not start at a "
+ "cylinder boundary.\n"
+ "DOS and Linux will interpret the contents differently.\n"));
+ }
+ }
+
+ while (moretodo) {
+ moretodo = 0;
+
+ if (!(s = get_sector(dev, fd, here)))
+ break;
+
+ if (!msdos_signature(s)) {
+ error(_("ERROR: sector %lu does not have an msdos signature\n"),
+ s->sectornumber);
+ break;
+ }
+ cp = s->data + 0x1be;
+
+ if (pno + 4 >= ARRAY_SIZE(z->partitions)) {
+ warnx(_("too many partitions - ignoring those past nr (%ld)\n"),
+ pno - 1);
+ break;
+ }
+
+ next = 0;
+
+ for (i = 0; i < 4; i++, cp += sizeof(struct partition)) {
+ partitions[pno].sector = here;
+ partitions[pno].offset = cp - s->data;
+ partitions[pno].ep = ep;
+ copy_to_part(cp, &p);
+ if (is_extended(p.sys_type)) {
+ partitions[pno].start = start + p.start_sect;
+ if (next)
+ warnx(_("tree of partitions?\n"));
+ else
+ next = partitions[pno].start; /* follow `upper' branch */
+ moretodo = 1;
+ } else {
+ partitions[pno].start = here + p.start_sect;
+ }
+ partitions[pno].size = p.nr_sects;
+ partitions[pno].ptype = DOS_TYPE;
+ partitions[pno].p = p;
+ pno++;
+ }
+ here = next;
+ }
+
+ z->partno = pno;
+}
+
+#define BSD_DISKMAGIC (0x82564557UL)
+#define BSD_MAXPARTITIONS 16
+#define BSD_FS_UNUSED 0
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+struct bsd_disklabel {
+ u32 d_magic;
+ char d_junk1[4];
+ char d_typename[16];
+ char d_packname[16];
+ char d_junk2[92];
+ u32 d_magic2;
+ char d_junk3[2];
+ u16 d_npartitions; /* number of partitions in following */
+ char d_junk4[8];
+ struct bsd_partition { /* the partition table */
+ u32 p_size; /* number of sectors in partition */
+ u32 p_offset; /* starting sector */
+ u32 p_fsize; /* filesystem basic fragment size */
+ u8 p_fstype; /* filesystem type, see below */
+ u8 p_frag; /* filesystem fragments per block */
+ u16 p_cpg; /* filesystem cylinders per group */
+ } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+static void
+bsd_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) {
+ struct bsd_disklabel *l;
+ struct bsd_partition *bp, *bp0;
+ unsigned long long start = ep->start;
+ struct sector *s;
+ struct part_desc *partitions = &(z->partitions[0]);
+ size_t pno = z->partno;
+
+ if (!(s = get_sector(dev, fd, start + 1)))
+ return;
+ l = (struct bsd_disklabel *)(s->data);
+ if (l->d_magic != BSD_DISKMAGIC || l->d_magic2 != BSD_DISKMAGIC)
+ return;
+
+ bp = bp0 = &l->d_partitions[0];
+ while (bp - bp0 < BSD_MAXPARTITIONS && bp - bp0 < l->d_npartitions) {
+ if (pno + 1 >= ARRAY_SIZE(z->partitions)) {
+ warnx(_("too many partitions - ignoring those "
+ "past nr (%ld)\n"), pno - 1);
+ break;
+ }
+ if (bp->p_fstype != BSD_FS_UNUSED) {
+ partitions[pno].start = bp->p_offset;
+ partitions[pno].size = bp->p_size;
+ partitions[pno].sector = start + 1;
+ partitions[pno].offset = (char *)bp - (char *)bp0;
+ partitions[pno].ep = 0;
+ partitions[pno].ptype = BSD_TYPE;
+ pno++;
+ }
+ bp++;
+ }
+ z->partno = pno;
+}
+
+static int
+msdos_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) {
+ int i;
+ char *cp;
+ struct partition pt;
+ struct sector *s;
+ struct part_desc *partitions = &(z->partitions[0]);
+ int pno = z->partno;
+ int bsd_later = 1;
+#ifdef __linux__
+ bsd_later = (get_linux_version() >= KERNEL_VERSION(2, 3, 40));
+#endif
+
+ if (!(s = get_sector(dev, fd, start)))
+ return 0;
+
+ if (!msdos_signature(s))
+ return 0;
+
+ cp = s->data + 0x1be;
+ copy_to_part(cp, &pt);
+
+ /* If I am not mistaken, recent kernels will hide this from us,
+ so we will never actually see traces of a Disk Manager */
+ if (pt.sys_type == DM6_PARTITION
+ || pt.sys_type == EZD_PARTITION
+ || pt.sys_type == DM6_AUX1PARTITION
+ || pt.sys_type == DM6_AUX3PARTITION) {
+ warnx(_("detected Disk Manager - unable to handle that\n"));
+ return 0;
+ }
+
+ unsigned int sig = *(unsigned short *)(s->data + 2);
+ if (sig <= 0x1ae
+ && *(unsigned short *)(s->data + sig) == 0x55aa
+ && (1 & *(unsigned char *)(s->data + sig + 2))) {
+ warnx(_("DM6 signature found - giving up\n"));
+ return 0;
+ }
+
+ for (pno = 0; pno < 4; pno++, cp += sizeof(struct partition)) {
+ partitions[pno].sector = start;
+ partitions[pno].offset = cp - s->data;
+ copy_to_part(cp, &pt);
+ partitions[pno].start = start + pt.start_sect;
+ partitions[pno].size = pt.nr_sects;
+ partitions[pno].ep = 0;
+ partitions[pno].p = pt;
+ }
+
+ z->partno = pno;
+
+ for (i = 0; i < 4; i++) {
+ if (is_extended(partitions[i].p.sys_type)) {
+ if (!partitions[i].size) {
+ warnx(_("strange..., an extended partition of size 0?\n"));
+ continue;
+ }
+ extended_partition(dev, fd, &partitions[i], z);
+ }
+ if (!bsd_later && is_bsd(partitions[i].p.sys_type)) {
+ if (!partitions[i].size) {
+ warnx(_("strange..., a BSD partition of size 0?\n"));
+ continue;
+ }
+ bsd_partition(dev, fd, &partitions[i], z);
+ }
+ }
+
+ if (bsd_later) {
+ for (i = 0; i < 4; i++) {
+ if (is_bsd(partitions[i].p.sys_type)) {
+ if (!partitions[i].size) {
+ warnx(_("strange..., a BSD partition of size 0?\n"));
+ continue;
+ }
+ bsd_partition(dev, fd, &partitions[i], z);
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int
+osf_partition(char *dev __attribute__ ((__unused__)),
+ int fd __attribute__ ((__unused__)),
+ unsigned long start __attribute__ ((__unused__)),
+ struct disk_desc *z __attribute__ ((__unused__))) {
+ return 0;
+}
+
+static int
+sun_partition(char *dev __attribute__ ((__unused__)),
+ int fd __attribute__ ((__unused__)),
+ unsigned long start __attribute__ ((__unused__)),
+ struct disk_desc *z __attribute__ ((__unused__))) {
+ return 0;
+}
+
+static int
+amiga_partition(char *dev __attribute__ ((__unused__)),
+ int fd __attribute__ ((__unused__)),
+ unsigned long start __attribute__ ((__unused__)),
+ struct disk_desc *z __attribute__ ((__unused__))) {
+ return 0;
+}
+
+static void
+get_partitions(char *dev, int fd, struct disk_desc *z) {
+ z->partno = 0;
+
+ if (!msdos_partition(dev, fd, 0, z)
+ && !osf_partition(dev, fd, 0, z)
+ && !sun_partition(dev, fd, 0, z)
+ && !amiga_partition(dev, fd, 0, z)) {
+ if (!opt_list)
+ warnx(_(" %s: unrecognized partition table type\n"), dev);
+ return;
+ }
+}
+
+static int
+write_partitions(char *dev, int fd, struct disk_desc *z) {
+ struct sector *s;
+ struct part_desc *partitions = &(z->partitions[0]), *p;
+ int pno = z->partno;
+
+ if (no_write) {
+ warnx(_("-n flag was given: Nothing changed\n"));
+ exit(0);
+ }
+
+ for (p = partitions; p < partitions + pno; p++) {
+ s = get_sector(dev, fd, p->sector);
+ if (!s)
+ return 0;
+ s->to_be_written = 1;
+ if (p->ptype == DOS_TYPE) {
+ copy_from_part(&(p->p), s->data + p->offset);
+ s->data[510] = 0x55;
+ s->data[511] = (unsigned char)0xaa;
+ }
+ }
+ if (save_sector_file) {
+ if (!save_sectors(dev, fd)) {
+ errx(EXIT_FAILURE, _("Failed saving the old sectors - aborting\n"));
+ return 0;
+ }
+ }
+ if (!write_sectors(dev, fd)) {
+ error(_("Failed writing the partition on %s\n"), dev);
+ return 0;
+ }
+ if (fsync(fd)) {
+ perror(dev);
+ error(_("Failed writing the partition on %s\n"), dev);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * F. The standard input
+ */
+
+/*
+ * Input format:
+ * <start> <size> <type> <bootable> <c,h,s> <c,h,s>
+ * Fields are separated by whitespace or comma or semicolon possibly
+ * followed by whitespace; initial and trailing whitespace is ignored.
+ * Numbers can be octal, decimal or hexadecimal, decimal is default
+ * The <c,h,s> parts can (and probably should) be omitted.
+ * Bootable is specified as [*|-], with as default not-bootable.
+ * Type is given in hex, without the 0x prefix, or is [E|S|L|X], where
+ * L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), and E
+ * is EXTENDED_PARTITION (5), X is LINUX_EXTENDED (85).
+ * The default value of start is the first nonassigned sector/cylinder/...
+ * The default value of size is as much as possible (until next
+ * partition or end-of-disk).
+ * .: end of chain of extended partitions.
+ *
+ * On interactive input an empty line means: all defaults.
+ * Otherwise empty lines are ignored.
+ */
+
+int eof, eob;
+
+struct dumpfld {
+ int fldno;
+ char *fldname;
+ int is_bool;
+} dumpflds[] = {
+ {
+ 0, "start", 0}, {
+ 1, "size", 0}, {
+ 2, "Id", 0}, {
+ 3, "bootable", 1}, {
+ 4, "bh", 0}, {
+ 5, "bs", 0}, {
+ 6, "bc", 0}, {
+ 7, "eh", 0}, {
+ 8, "es", 0}, {
+ 9, "ec", 0}
+};
+
+/*
+ * Read a line, split it into fields
+ *
+ * (some primitive handwork, but a more elaborate parser seems
+ * unnecessary)
+ */
+#define RD_EOF (-1)
+#define RD_CMD (-2)
+
+static int
+read_stdin(char **fields, char *line, int fieldssize, int linesize) {
+ char *lp, *ip;
+ int c, fno;
+
+ /* boolean true and empty string at start */
+ line[0] = '*';
+ line[1] = 0;
+ for (fno = 0; fno < fieldssize; fno++)
+ fields[fno] = line + 1;
+ fno = 0;
+
+ /* read a line from stdin */
+ lp = fgets(line + 2, linesize - 2, stdin);
+ if (lp == NULL) {
+ eof = 1;
+ return RD_EOF;
+ }
+ if (!(lp = strchr(lp, '\n')))
+ errx(EXIT_FAILURE, _("long or incomplete input line - quitting"));
+ *lp = 0;
+
+ /* remove comments, if any */
+ if ((lp = strchr(line + 2, '#')) != 0)
+ *lp = 0;
+
+ /* recognize a few commands - to be expanded */
+ if (!strcmp(line + 2, "unit: sectors")) {
+ specified_format = F_SECTOR;
+ return RD_CMD;
+ }
+
+ /* dump style? - then bad input is fatal */
+ if ((ip = strchr(line + 2, ':')) != 0) {
+ struct dumpfld *d;
+
+ nxtfld:
+ ip++;
+ while (isspace(*ip))
+ ip++;
+ if (*ip == 0)
+ return fno;
+ for (d = dumpflds; (size_t) (d - dumpflds) < ARRAY_SIZE(dumpflds); d++) {
+ if (!strncmp(ip, d->fldname, strlen(d->fldname))) {
+ ip += strlen(d->fldname);
+ while (isspace(*ip))
+ ip++;
+ if (d->is_bool)
+ fields[d->fldno] = line;
+ else if (*ip == '=') {
+ while (isspace(*++ip)) ;
+ fields[d->fldno] = ip;
+ while (isalnum(*ip)) /* 0x07FF */
+ ip++;
+ } else
+ errx(EXIT_FAILURE, _("input error: `=' expected after %s field"),
+ d->fldname);
+ if (fno <= d->fldno)
+ fno = d->fldno + 1;
+ if (*ip == 0)
+ return fno;
+ if (*ip != ',' && *ip != ';')
+ errx(EXIT_FAILURE, _("input error: unexpected character %c after %s field"),
+ *ip, d->fldname);
+ *ip = 0;
+ goto nxtfld;
+ }
+ }
+ errx(EXIT_FAILURE, _("unrecognized input: %s"), ip);
+ }
+
+ /* split line into fields */
+ lp = ip = line + 2;
+ fields[fno++] = lp;
+ while ((c = *ip++) != 0) {
+ if (!lp[-1] && (c == '\t' || c == ' ')) ;
+ else if (c == '\t' || c == ' ' || c == ',' || c == ';') {
+ *lp++ = 0;
+ if (fno < fieldssize)
+ fields[fno++] = lp;
+ continue;
+ } else
+ *lp++ = c;
+ }
+
+ if (lp == fields[fno - 1])
+ fno--;
+ return fno;
+}
+
+/* read a number, use default if absent */
+/* a sign gives an offset from the default */
+static int
+get_ul(char *u, unsigned long *up, unsigned long def, int base) {
+ char *nu;
+ int sign = 0;
+ unsigned long val;
+
+ if (*u == '+') {
+ sign = 1;
+ u++;
+ } else if (*u == '-') {
+ sign = -1;
+ u++;
+ }
+ if (*u) {
+ errno = 0;
+ val = strtoul(u, &nu, base);
+ if (errno == ERANGE) {
+ warnx(_("number too big\n"));
+ return -1;
+ }
+ if (*nu) {
+ warnx(_("trailing junk after number\n"));
+ return -1;
+ }
+ if (sign == 1)
+ val = def + val;
+ else if (sign == -1)
+ val = def - val;
+ *up = val;
+ } else
+ *up = def;
+ return 0;
+}
+
+
+/* read a number, use default if absent */
+/* a sign gives an offset from the default */
+static int
+get_ull(char *u, unsigned long long *up, unsigned long long def, int base) {
+ char *nu;
+ int sign = 0;
+ unsigned long long val;
+
+ if (*u == '+') {
+ sign = 1;
+ u++;
+ } else if (*u == '-') {
+ sign = -1;
+ u++;
+ }
+ if (*u) {
+ errno = 0;
+ val = strtoull(u, &nu, base);
+ if (errno == ERANGE) {
+ warnx(_("number too big\n"));
+ return -1;
+ }
+ if (*nu) {
+ warnx(_("trailing junk after number\n"));
+ return -1;
+ }
+ if (sign == 1)
+ val = def + val;
+ else if (sign == -1)
+ val = def - val;
+ *up = val;
+ } else
+ *up = def;
+ return 0;
+}
+
+
+/* There are two common ways to structure extended partitions:
+ as nested boxes, and as a chain. Sometimes the partitions
+ must be given in order. Sometimes all logical partitions
+ must lie inside the outermost extended partition.
+NESTED: every partition is contained in the surrounding partitions
+ and is disjoint from all others.
+CHAINED: every data partition is contained in the surrounding partitions
+ and disjoint from all others, but extended partitions may lie outside
+ (insofar as allowed by all_logicals_inside_outermost_extended).
+ONESECTOR: all data partitions are mutually disjoint; extended partitions
+ each use one sector only (except perhaps for the outermost one).
+*/
+int partitions_in_order = 0;
+int all_logicals_inside_outermost_extended = 1;
+enum { NESTED, CHAINED, ONESECTOR } boxes = NESTED;
+
+/* find the default value for <start> - assuming entire units */
+static unsigned long long
+first_free(int pno, int is_extended, struct part_desc *ep, int format,
+ unsigned long long mid, struct disk_desc *z) {
+ unsigned long long ff, fff;
+ unsigned long unit = unitsize(format);
+ struct part_desc *partitions = &(z->partitions[0]), *pp = 0;
+
+ /* if containing ep undefined, look at its container */
+ if (ep && ep->p.sys_type == EMPTY_PARTITION)
+ ep = ep->ep;
+
+ if (ep) {
+ if (boxes == NESTED || (boxes == CHAINED && !is_extended))
+ pp = ep;
+ else if (all_logicals_inside_outermost_extended)
+ pp = outer_extended_partition(ep);
+ }
+#if 0
+ ff = pp ? (pp->start + unit - 1) / unit : 0;
+#else
+ /* rounding up wastes almost an entire cylinder - round down
+ and leave it to compute_start_sect() to fix the difference */
+ ff = pp ? pp->start / unit : 0;
+#endif
+ /* MBR and 1st sector of an extended partition are never free */
+ if (unit == 1)
+ ff++;
+
+ again:
+ for (pp = partitions; pp < partitions + pno; pp++) {
+ if (!is_parent(pp, ep) && pp->size > 0) {
+ if ((partitions_in_order || pp->start / unit <= ff
+ || (mid && pp->start / unit <= mid))
+ && (fff = (pp->start + pp->size + unit - 1) / unit) > ff) {
+ ff = fff;
+ goto again;
+ }
+ }
+ }
+
+ return ff;
+}
+
+/* find the default value for <size> - assuming entire units */
+static unsigned long long
+max_length(int pno, int is_extended, struct part_desc *ep, int format,
+ unsigned long long start, struct disk_desc *z) {
+ unsigned long long fu;
+ unsigned long unit = unitsize(format);
+ struct part_desc *partitions = &(z->partitions[0]), *pp = 0;
+
+ /* if containing ep undefined, look at its container */
+ if (ep && ep->p.sys_type == EMPTY_PARTITION)
+ ep = ep->ep;
+
+ if (ep) {
+ if (boxes == NESTED || (boxes == CHAINED && !is_extended))
+ pp = ep;
+ else if (all_logicals_inside_outermost_extended)
+ pp = outer_extended_partition(ep);
+ }
+ fu = pp ? (pp->start + pp->size) / unit : get_disksize(format);
+
+ for (pp = partitions; pp < partitions + pno; pp++)
+ if (!is_parent(pp, ep) && pp->size > 0
+ && pp->start / unit >= start && pp->start / unit < fu)
+ fu = pp->start / unit;
+
+ return (fu > start) ? fu - start : 0;
+}
+
+/* compute starting sector of a partition inside an extended one */
+/* return 0 on failure */
+/* ep is 0 or points to surrounding extended partition */
+static int
+compute_start_sect(struct part_desc *p, struct part_desc *ep) {
+ unsigned long long base;
+ int inc = (DOS && B.sectors) ? B.sectors : 1;
+ long long delta;
+
+ if (ep && p->start + p->size >= ep->start + 1)
+ delta = p->start - ep->start - inc;
+ else if (p->start == 0 && p->size > 0)
+ delta = -inc;
+ else
+ delta = 0;
+
+ if (delta < 0) {
+ unsigned long long old_size = p->size;
+ p->start -= delta;
+ p->size += delta;
+ if (is_extended(p->p.sys_type) && boxes == ONESECTOR)
+ p->size = inc;
+ else if ((long long) old_size <= -delta) {
+ my_warn(_("no room for partition descriptor\n"));
+ return 0;
+ }
+ }
+ base = (!ep ? 0
+ : (is_extended(p->p.sys_type) ?
+ outer_extended_partition(ep) : ep)->start);
+ p->ep = ep;
+ if (p->p.sys_type == EMPTY_PARTITION && p->size == 0) {
+ p->p.start_sect = 0;
+ p->p.begin_chs = zero_chs;
+ p->p.end_chs = zero_chs;
+ } else {
+ p->p.start_sect = p->start - base;
+ p->p.begin_chs = ulong_to_chs(p->start, B);
+ p->p.end_chs = ulong_to_chs(p->start + p->size - 1, B);
+ }
+ p->p.nr_sects = p->size;
+ return 1;
+}
+
+/* build the extended partition surrounding a given logical partition */
+static int
+build_surrounding_extended(struct part_desc *p, struct part_desc *ep,
+ struct disk_desc *z) {
+ int inc = (DOS && B.sectors) ? B.sectors : 1;
+ int format = F_SECTOR;
+ struct part_desc *p0 = &(z->partitions[0]), *eep = ep->ep;
+
+ if (boxes == NESTED) {
+ ep->start = first_free(ep - p0, 1, eep, format, p->start, z);
+ ep->size = max_length(ep - p0, 1, eep, format, ep->start, z);
+ if (ep->start > p->start || ep->start + ep->size < p->start + p->size) {
+ my_warn(_("cannot build surrounding extended partition\n"));
+ return 0;
+ }
+ } else {
+ ep->start = p->start;
+ if (boxes == CHAINED)
+ ep->size = p->size;
+ else
+ ep->size = inc;
+ }
+
+ ep->p.nr_sects = ep->size;
+ ep->p.bootable = 0;
+ ep->p.sys_type = EXTENDED_PARTITION;
+ if (!compute_start_sect(ep, eep) || !compute_start_sect(p, ep)) {
+ ep->p.sys_type = EMPTY_PARTITION;
+ ep->size = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+read_line(int pno, struct part_desc *ep, char *dev, int interactive,
+ struct disk_desc *z) {
+ char line[1000];
+ char *fields[11];
+ int fno, pct = pno % 4;
+ struct part_desc p, *orig;
+ unsigned long long ff, ff1, ul, ml, ml1, def;
+ int format, lpno, is_extd;
+
+ if (eof || eob)
+ return -1;
+
+ lpno = index_to_linux(pno, z);
+
+ if (interactive) {
+ if (pct == 0 && (show_extended || pno == 0))
+ my_warn("\n");
+ my_warn("%s:", partname(dev, lpno, 10));
+ }
+
+ /* read input line - skip blank lines when reading from a file */
+ do {
+ fno = read_stdin(fields, line, ARRAY_SIZE(fields), ARRAY_SIZE(line));
+ } while (fno == RD_CMD || (fno == 0 && !interactive));
+ if (fno == RD_EOF) {
+ return -1;
+ } else if (fno > 10 && *(fields[10]) != 0) {
+ warnx(_("too many input fields\n"));
+ return 0;
+ }
+
+ if (fno == 1 && !strcmp(fields[0], ".")) {
+ eob = 1;
+ return -1;
+ }
+
+ /* use specified format, but round to cylinders if F_MEGABYTE specified */
+ format = 0;
+ if (B.cylindersize && specified_format == F_MEGABYTE && !Linux)
+ format = F_CYLINDER;
+
+ orig = (one_only ? &(oldp.partitions[pno]) : 0);
+
+ p = zero_part_desc;
+ p.ep = ep;
+
+ /* first read the type - we need to know whether it is extended */
+ /* stop reading when input blank (defaults) and all is full */
+ is_extd = 0;
+ if (fno == 0) { /* empty line */
+ if (orig && is_extended(orig->p.sys_type))
+ is_extd = 1;
+ ff = first_free(pno, is_extd, ep, format, 0, z);
+ ml = max_length(pno, is_extd, ep, format, ff, z);
+ if (ml == 0 && is_extd == 0) {
+ is_extd = 1;
+ ff = first_free(pno, is_extd, ep, format, 0, z);
+ ml = max_length(pno, is_extd, ep, format, ff, z);
+ }
+ if (ml == 0 && pno >= 4) {
+ /* no free blocks left - don't read any further */
+ my_warn(_("No room for more\n"));
+ return -1;
+ }
+ }
+ if (fno < 3 || !*(fields[2]))
+ ul = orig ? orig->p.sys_type :
+ (is_extd || (pno > 3 && pct == 1 && show_extended))
+ ? EXTENDED_PARTITION : LINUX_NATIVE;
+ else if (!strcmp(fields[2], "L"))
+ ul = LINUX_NATIVE;
+ else if (!strcmp(fields[2], "S"))
+ ul = LINUX_SWAP;
+ else if (!strcmp(fields[2], "E"))
+ ul = EXTENDED_PARTITION;
+ else if (!strcmp(fields[2], "X"))
+ ul = LINUX_EXTENDED;
+ else if (get_ull(fields[2], &ul, LINUX_NATIVE, 16))
+ return 0;
+ if (ul > 255) {
+ my_warn(_("Illegal type\n"));
+ return 0;
+ }
+ p.p.sys_type = ul;
+ is_extd = is_extended(ul);
+
+ /* find start */
+ ff = first_free(pno, is_extd, ep, format, 0, z);
+ ff1 = ff * unitsize(format);
+ def = orig ? orig->start : (pno > 4 && pct > 1) ? 0 : ff1;
+ if (fno < 1 || !*(fields[0]))
+ p.start = def;
+ else {
+ if (get_ull(fields[0], &ul, def / unitsize(0), 0))
+ return 0;
+ p.start = ul * unitsize(0);
+ p.start -= (p.start % unitsize(format));
+ }
+
+ /* find length */
+ ml = max_length(pno, is_extd, ep, format, p.start / unitsize(format), z);
+ ml1 = ml * unitsize(format);
+ def = orig ? orig->size : (pno > 4 && pct > 1) ? 0 : ml1;
+ if (fno < 2 || !*(fields[1]))
+ p.size = def;
+ else if (!strcmp(fields[1], "+"))
+ p.size = ml1;
+ else {
+ if (get_ull(fields[1], &ul, def / unitsize(0), 0))
+ return 0;
+ p.size = ul * unitsize(0) + unitsize(format) - 1;
+ p.size -= (p.size % unitsize(format));
+ }
+ if (p.size > ml1) {
+ my_warn(_("Warning: given size (%lu) exceeds max allowable size (%lu)\n"),
+ (p.size + unitsize(0) - 1) / unitsize(0), ml1 / unitsize(0));
+ if (!force)
+ return 0;
+ }
+ if (p.size == 0 && pno >= 4 && (fno < 2 || !*(fields[1]))) {
+ my_warn(_("Warning: empty partition\n"));
+ if (!force)
+ return 0;
+ }
+ p.p.nr_sects = p.size;
+
+ if (p.size == 0 && !orig) {
+ if (fno < 1 || !*(fields[0]))
+ p.start = 0;
+ if (fno < 3 || !*(fields[2]))
+ p.p.sys_type = EMPTY_PARTITION;
+ }
+
+ if (p.start < ff1 && p.size > 0) {
+ my_warn(_("Warning: bad partition start (earliest %lu)\n"),
+ (ff1 + unitsize(0) - 1) / unitsize(0));
+ if (!force)
+ return 0;
+ }
+
+ if (fno < 4 || !*(fields[3]))
+ ul = (orig ? orig->p.bootable : 0);
+ else if (!strcmp(fields[3], "-"))
+ ul = 0;
+ else if (!strcmp(fields[3], "*") || !strcmp(fields[3], "+"))
+ ul = 0x80;
+ else {
+ my_warn(_("unrecognized bootable flag - choose - or *\n"));
+ return 0;
+ }
+ p.p.bootable = ul;
+
+ if (ep && ep->p.sys_type == EMPTY_PARTITION) {
+ if (!build_surrounding_extended(&p, ep, z))
+ return 0;
+ } else if (!compute_start_sect(&p, ep))
+ return 0;
+
+ {
+ longchs aa = chs_to_longchs(p.p.begin_chs), bb;
+
+ if (fno < 5) {
+ bb = aa;
+ } else if (fno < 7) {
+ my_warn(_("partial c,h,s specification?\n"));
+ return 0;
+ } else if (get_ul(fields[4], &bb.c, aa.c, 0) ||
+ get_ul(fields[5], &bb.h, aa.h, 0) ||
+ get_ul(fields[6], &bb.s, aa.s, 0))
+ return 0;
+ p.p.begin_chs = longchs_to_chs(bb, B);
+ }
+ {
+ longchs aa = chs_to_longchs(p.p.end_chs), bb;
+
+ if (fno < 8) {
+ bb = aa;
+ } else if (fno < 10) {
+ my_warn(_("partial c,h,s specification?\n"));
+ return 0;
+ } else if (get_ul(fields[7], &bb.c, aa.c, 0) ||
+ get_ul(fields[8], &bb.h, aa.h, 0) ||
+ get_ul(fields[9], &bb.s, aa.s, 0))
+ return 0;
+ p.p.end_chs = longchs_to_chs(bb, B);
+ }
+
+ if (pno > 3 && p.size && show_extended && p.p.sys_type != EMPTY_PARTITION
+ && (is_extended(p.p.sys_type) != (pct == 1))) {
+ my_warn(_("Extended partition not where expected\n"));
+ if (!force)
+ return 0;
+ }
+
+ z->partitions[pno] = p;
+ if (pno >= z->partno)
+ z->partno += 4; /* reqd for out_partition() */
+
+ if (interactive)
+ out_partition(dev, 0, &(z->partitions[pno]), z, B);
+
+ return 1;
+}
+
+/* ep either points to the extended partition to contain this one,
+ or to the empty partition that may become extended or is 0 */
+static int
+read_partition(char *dev, int interactive, int pno, struct part_desc *ep,
+ struct disk_desc *z) {
+ struct part_desc *p = &(z->partitions[pno]);
+ int i;
+
+ if (one_only) {
+ *p = oldp.partitions[pno];
+ if (one_only_pno != pno)
+ goto ret;
+ } else if (!show_extended && pno > 4 && pno % 4)
+ goto ret;
+
+ while (!(i = read_line(pno, ep, dev, interactive, z)))
+ if (!interactive)
+ errx(EXIT_FAILURE, _("bad input"));
+ if (i < 0) {
+ p->ep = ep;
+ return 0;
+ }
+
+ ret:
+ p->ep = ep;
+ if (pno >= z->partno)
+ z->partno += 4;
+ return 1;
+}
+
+static void
+read_partition_chain(char *dev, int interactive, struct part_desc *ep,
+ struct disk_desc *z) {
+ int i;
+ size_t base;
+
+ eob = 0;
+ while (1) {
+ base = z->partno;
+ if (base + 4 > ARRAY_SIZE(z->partitions)) {
+ warnx(_("too many partitions\n"));
+ break;
+ }
+ for (i = 0; i < 4; i++)
+ if (!read_partition(dev, interactive, base + i, ep, z))
+ return;
+ for (i = 0; i < 4; i++) {
+ ep = &(z->partitions[base + i]);
+ if (is_extended(ep->p.sys_type) && ep->size)
+ break;
+ }
+ if (i == 4) {
+ /* nothing found - maybe an empty partition is going
+ to be extended */
+ if (one_only || show_extended)
+ break;
+ ep = &(z->partitions[base + 1]);
+ if (ep->size || ep->p.sys_type != EMPTY_PARTITION)
+ break;
+ }
+ }
+}
+
+static void
+read_input(char *dev, int interactive, struct disk_desc *z) {
+ size_t i;
+ struct part_desc *partitions = &(z->partitions[0]), *ep;
+
+ for (i = 0; i < ARRAY_SIZE(z->partitions); i++)
+ partitions[i] = zero_part_desc;
+ z->partno = 0;
+
+ if (interactive)
+ my_warn(_("Input in the following format; absent fields get a default value.\n"
+ "<start> <size> <type [E,S,L,X,hex]> <bootable [-,*]> <c,h,s> <c,h,s>\n"
+ "Usually you only need to specify <start> and <size> (and perhaps <type>).\n"));
+ eof = 0;
+
+ for (i = 0; i < 4; i++)
+ read_partition(dev, interactive, i, 0, z);
+ for (i = 0; i < 4; i++) {
+ ep = partitions + i;
+ if (is_extended(ep->p.sys_type) && ep->size)
+ read_partition_chain(dev, interactive, ep, z);
+ }
+ add_sector_and_offset(z);
+}
+
+/*
+ * G. The command line
+ */
+static void usage(FILE * out)
+{
+ fputs(_("\nUsage:\n"), out);
+ fprintf(out,
+ _(" %s [options] <device> [...]\n"), program_invocation_short_name);
+
+ fputs(_("\nOptions:\n"), out);
+ fputs(_(" -s, --show-size list size of a partition\n"
+ " -c, --id change or print partition Id\n"
+ " --change-id change Id\n"
+ " --print-id print Id\n"), out);
+ fputs(_(" -l, --list list partitions of each device\n"
+ " -d, --dump idem, but in a format suitable for later input\n"
+ " -i, --increment number cylinders etc. from 1 instead of from 0\n"
+ " -u, --unit <letter> units to be used; <letter> can be one of\n"
+ " S (sectors), C (cylinders), B (blocks), or M (MB)\n"), out);
+ fputs(_(" -1, --one-only reserved option that does nothing currently\n"
+ " -T, --list-types list the known partition types\n"
+ " -D, --DOS for DOS-compatibility: waste a little space\n"
+ " -E, --DOS-extended DOS extended partition compatibility\n"
+ " -R, --re-read make the kernel reread the partition table\n"), out);
+ fputs(_(" -N <number> change only the partition with this <number>\n"
+ " -n do not actually write to disk\n"
+ " -O <file> save the sectors that will be overwritten to <file>\n"
+ " -I <file> restore sectors from <file>\n"), out);
+ fputs(_(" -V, --verify check that the listed partitions are reasonable\n"
+ " -v, --version display version information and exit\n"
+ " -h, --help display this help text and exit\n"), out);
+
+ fputs(_("\nDangerous options:\n"), out);
+ fputs(_(" -f, --force disable all consistency checking\n"
+ " --no-reread do not check whether the partition is in use\n"
+ " -q, --quiet suppress warning messages\n"
+ " -L, --Linux do not complain about things irrelevant for Linux\n"), out);
+ fputs(_(" -g, --show-geometry print the kernel's idea of the geometry\n"
+ " -G, --show-pt-geometry print geometry guessed from the partition table\n"), out);
+ fputs(_(" -A, --activate[=<device>] activate bootable flag\n"
+ " -U, --unhide[=<dev>] set partition unhidden\n"
+ " -x, --show-extended also list extended partitions in the output,\n"
+ " or expect descriptors for them in the input\n"), out);
+ fputs(_(" --leave-last do not allocate the last cylinder\n"
+ " --IBM same as --leave-last\n"), out);
+ fputs(_(" --in-order partitions are in order\n"
+ " --not-in-order partitions are not in order\n"
+ " --inside-outer all logicals inside outermost extended\n"
+ " --not-inside-outer not all logicals inside outermost extended\n"), out);
+ fputs(_(" --nested every partition is disjoint from all others\n"
+ " --chained like nested, but extended partitions may lie outside\n"
+ " --onesector partitions are mutually disjoint\n"), out);
+
+ fputs(_("\nOverride the detected geometry using:\n"
+ " -C, --cylinders <number> set the number of cylinders to use\n"
+ " -H, --heads <number> set the number of heads to use\n"
+ " -S, --sectors <number> set the number of sectors to use\n\n"), out);
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void
+activate_usage(char *progn) {
+ puts(_("Usage:"));
+ printf(_("%s device list active partitions on device\n"), progn);
+ printf(_("%s device n1 n2 ... activate partitions n1 ..., inactivate the rest\n"),
+ progn);
+ printf(_("%s -An device activate partition n, inactivate the other ones\n"),
+ PROGNAME);
+ exit(1);
+}
+
+static void
+unhide_usage(char *progn __attribute__ ((__unused__))) {
+ exit(1);
+}
+
+static const char short_opts[] = "cdfghilnqsu:vx1A::C:DGH:I:LN:O:RS:TU::V";
+
+#define PRINT_ID 0400
+#define CHANGE_ID 01000
+
+enum {
+ OPT_NO_REREAD = CHAR_MAX + 1,
+ OPT_LEAVE_LAST,
+ OPT_IN_ORDER,
+ OPT_NOT_IN_ORDER,
+ OPT_INSIDE_OUTER,
+ OPT_NOT_INSIDE_OUTER,
+ OPT_NESTED,
+ OPT_CHAINED,
+ OPT_ONESECTOR
+};
+
+static const struct option long_opts[] = {
+ { "change-id", no_argument, NULL, 'c' + CHANGE_ID },
+ { "print-id", no_argument, NULL, 'c' + PRINT_ID },
+ { "id", no_argument, NULL, 'c' },
+ { "dump", no_argument, NULL, 'd' },
+ { "force", no_argument, NULL, 'f' },
+ { "show-geometry", no_argument, NULL, 'g' },
+ { "help", no_argument, NULL, 'h' },
+ { "increment", no_argument, NULL, 'i' },
+ { "list", no_argument, NULL, 'l' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "show-size", no_argument, NULL, 's' },
+ { "unit", required_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'v' },
+ { "show-extended", no_argument, NULL, 'x' },
+ { "one-only", no_argument, NULL, '1' },
+ { "cylinders", required_argument, NULL, 'C' },
+ { "heads", required_argument, NULL, 'H' },
+ { "sectors", required_argument, NULL, 'S' },
+ { "show-pt-geometry", no_argument, NULL, 'G' },
+ { "activate", optional_argument, NULL, 'A' },
+ { "DOS", no_argument, NULL, 'D' },
+ { "DOS-extended", no_argument, NULL, 'E' },
+ { "Linux", no_argument, NULL, 'L' },
+ { "re-read", no_argument, NULL, 'R' },
+ { "list-types", no_argument, NULL, 'T' },
+ { "unhide", optional_argument, NULL, 'U' },
+ { "no-reread", no_argument, NULL, OPT_NO_REREAD },
+ { "IBM", no_argument, NULL, OPT_LEAVE_LAST },
+ { "leave-last", no_argument, NULL, OPT_LEAVE_LAST },
+/* dangerous flags - not all completely implemented */
+ { "in-order", no_argument, NULL, OPT_IN_ORDER },
+ { "not-in-order", no_argument, NULL, OPT_NOT_IN_ORDER },
+ { "inside-outer", no_argument, NULL, OPT_INSIDE_OUTER },
+ { "not-inside-outer", no_argument, NULL, OPT_NOT_INSIDE_OUTER },
+ { "nested", no_argument, NULL, OPT_NESTED },
+ { "chained", no_argument, NULL, OPT_CHAINED },
+ { "onesector", no_argument, NULL, OPT_ONESECTOR },
+ { NULL, 0, NULL, 0 }
+};
+
+static int is_ide_cdrom_or_tape(char *device)
+{
+ int fd, ret;
+
+ if ((fd = open(device, O_RDONLY)) < 0)
+ return 0;
+ ret = blkdev_is_cdrom(fd);
+
+ close(fd);
+ return ret;
+}
+
+static char *
+nextproc(FILE * procf) {
+ static char devname[256];
+ char line[1024], ptname[128 + 1];
+ int ma, mi;
+ unsigned long long sz;
+
+ if (procf == NULL)
+ return NULL;
+ while (fgets(line, sizeof(line), procf) != NULL) {
+ if (sscanf(line, " %d %d %llu %128[^\n ]", &ma, &mi, &sz, ptname) != 4)
+ continue;
+ snprintf(devname, sizeof(devname), "/dev/%s", ptname);
+ if (!is_whole_disk(devname))
+ continue;
+ return canonicalize_path(devname);
+ }
+
+ return NULL;
+}
+
+static void
+gpt_warning(char *dev, int warn_only) {
+ if (force)
+ warn_only = 1;
+
+ if (dev && gpt_probe_signature_devname(dev)) {
+ fflush(stdout);
+ fprintf(stderr,
+ _("\nWARNING: GPT (GUID Partition Table) detected on '%s'! "
+ "The util sfdisk doesn't support GPT. Use GNU Parted.\n\n"),
+ dev);
+ if (!warn_only) {
+ fprintf(stderr,
+ _("Use the --force flag to overrule this check.\n"));
+ exit(1);
+ }
+ }
+}
+
+static void do_list(char *dev, int silent);
+static void do_size(char *dev, int silent);
+static void do_geom(char *dev, int silent);
+static void do_pt_geom(char *dev, int silent);
+static void do_fdisk(char *dev);
+static void do_reread(char *dev);
+static void do_change_id(char *dev, char *part, char *id);
+static void do_unhide(char **av, int ac, char *arg);
+static void do_activate(char **av, int ac, char *arg);
+
+unsigned long long total_size;
+
+int
+main(int argc, char **argv) {
+ char *progn;
+ int c;
+ char *dev;
+ int opt_size = 0;
+ int opt_out_geom = 0;
+ int opt_out_pt_geom = 0;
+ int opt_reread = 0;
+ int activate = 0;
+ int do_id = 0;
+ int unhide = 0;
+ int fdisk = 0;
+ char *activatearg = 0;
+ char *unhidearg = 0;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ if (argc < 1)
+ errx(EXIT_FAILURE, _("no command?"));
+ if ((progn = strrchr(argv[0], '/')) == NULL)
+ progn = argv[0];
+ else
+ progn++;
+ if (!strcmp(progn, "activate"))
+ activate = 1; /* equivalent to `sfdisk -A' */
+#if 0 /* not important enough to deserve a name */
+ else if (!strcmp(progn, "unhide"))
+ unhide = 1; /* equivalent to `sfdisk -U' */
+#endif
+ else
+ fdisk = 1;
+
+ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'f':
+ force = 1;
+ break; /* does not imply quiet */
+ case 'g':
+ opt_out_geom = 1;
+ break;
+ case 'G':
+ opt_out_pt_geom = 1;
+ break;
+ case 'i':
+ increment = 1;
+ break;
+ case 'c':
+ case 'c' + PRINT_ID:
+ case 'c' + CHANGE_ID:
+ do_id = c;
+ break;
+ case 'd':
+ dump = 1; /* fall through */
+ case 'l':
+ opt_list = 1;
+ break;
+ case 'n':
+ no_write = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 's':
+ opt_size = 1;
+ break;
+ case 'u':
+ set_format(*optarg);
+ break;
+ case 'v':
+ printf(_("%s from %s\n"), program_invocation_short_name,
+ PACKAGE_STRING);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ return EXIT_SUCCESS;
+ case 'x':
+ show_extended = 1;
+ break;
+ case 'A':
+ activatearg = optarg;
+ activate = 1;
+ break;
+ case 'C':
+ U.cylinders = strtoul_or_err(optarg, _("invalid cylinders argument"));
+ break;
+ case 'D':
+ DOS = 1;
+ break;
+ case 'E':
+ DOS_extended = 1;
+ break;
+ case 'H':
+ U.heads = strtoul_or_err(optarg, _("invalid heads argument"));
+ break;
+ case 'L':
+ Linux = 1;
+ break;
+ case 'N':
+ one_only = strtol_or_err(optarg, _("invalid number of partitions argument"));
+ break;
+ case 'I':
+ restore_sector_file = optarg;
+ break;
+ case 'O':
+ save_sector_file = optarg;
+ break;
+ case 'R':
+ opt_reread = 1;
+ break;
+ case 'S':
+ U.sectors = strtoul_or_err(optarg, _("invalid sectors argument"));
+ break;
+ case 'T':
+ list_types();
+ exit(0);
+ case 'U':
+ unhidearg = optarg;
+ unhide = 1;
+ break;
+ case 'V':
+ verify = 1;
+ break;
+ default:
+ usage(stderr);
+ break;
+
+ /* dangerous flags */
+ case OPT_IN_ORDER:
+ partitions_in_order = 1;
+ break;
+ case OPT_NOT_IN_ORDER:
+ partitions_in_order = 0;
+ break;
+ case OPT_INSIDE_OUTER:
+ all_logicals_inside_outermost_extended = 1;
+ break;
+ case OPT_NOT_INSIDE_OUTER:
+ all_logicals_inside_outermost_extended = 0;
+ break;
+ case OPT_NESTED:
+ boxes = NESTED;
+ break;
+ case OPT_CHAINED:
+ boxes = CHAINED;
+ break;
+ case OPT_ONESECTOR:
+ boxes = ONESECTOR;
+ break;
+
+ /* more flags */
+ case OPT_NO_REREAD:
+ no_reread = 1;
+ break;
+ case OPT_LEAVE_LAST:
+ leave_last = 1;
+ break;
+ }
+ }
+
+ if (optind == argc &&
+ (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify)) {
+ FILE *procf;
+
+ /* try all known devices */
+ total_size = 0;
+
+ procf = fopen(_PATH_PROC_PARTITIONS, "r");
+ if (!procf)
+ fprintf(stderr, _("cannot open %s\n"), _PATH_PROC_PARTITIONS);
+ else {
+ while ((dev = nextproc(procf)) != NULL) {
+ if (!is_ide_cdrom_or_tape(dev)) {
+ gpt_warning(dev, 1);
+ if (opt_out_geom)
+ do_geom(dev, 1);
+ if (opt_out_pt_geom)
+ do_pt_geom(dev, 1);
+ if (opt_size)
+ do_size(dev, 1);
+ if (opt_list || verify)
+ do_list(dev, 1);
+ }
+ free(dev);
+ }
+ fclose(procf);
+ }
+
+ if (opt_size)
+ printf(_("total: %llu blocks\n"), total_size);
+
+ exit(exit_status);
+ }
+
+ if (optind == argc) {
+ if (activate)
+ activate_usage(fdisk ? "sfdisk -A" : progn);
+ else if (unhide)
+ unhide_usage(fdisk ? "sfdisk -U" : progn);
+ else
+ usage(stderr);
+ }
+
+ if (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify) {
+ while (optind < argc) {
+ gpt_warning(argv[optind], 1);
+ if (opt_out_geom)
+ do_geom(argv[optind], 0);
+ if (opt_out_pt_geom)
+ do_pt_geom(argv[optind], 0);
+ if (opt_size)
+ do_size(argv[optind], 0);
+ if (opt_list || verify)
+ do_list(argv[optind], 0);
+ optind++;
+ }
+ exit(exit_status);
+ }
+
+ if (optind != argc - 1)
+ gpt_warning(argv[optind], 0);
+
+ if (activate) {
+ do_activate(argv + optind, argc - optind, activatearg);
+ exit(exit_status);
+ }
+ if (unhide) {
+ do_unhide(argv + optind, argc - optind, unhidearg);
+ exit(exit_status);
+ }
+ if (do_id) {
+ if ((do_id & PRINT_ID) != 0 && optind != argc - 2)
+ errx(EXIT_FAILURE, _("usage: sfdisk --print-id device partition-number"));
+ else if ((do_id & CHANGE_ID) != 0 && optind != argc - 3)
+ errx(EXIT_FAILURE, _("usage: sfdisk --change-id device partition-number Id"));
+ else if (optind != argc - 3 && optind != argc - 2)
+ errx(EXIT_FAILURE, _("usage: sfdisk --id device partition-number [Id]"));
+ do_change_id(argv[optind], argv[optind + 1],
+ (optind == argc - 2) ? 0 : argv[optind + 2]);
+ exit(exit_status);
+ }
+
+ if (optind != argc - 1)
+ errx(EXIT_FAILURE, _("can specify only one device (except with -l or -s)"));
+ dev = argv[optind];
+
+ if (opt_reread)
+ do_reread(dev);
+ else if (restore_sector_file)
+ restore_sectors(dev);
+ else
+ do_fdisk(dev);
+
+ return 0;
+}
+
+/*
+ * H. Listing the current situation
+ */
+
+static int
+my_open(char *dev, int rw, int silent) {
+ int fd, mode;
+
+ mode = (rw ? O_RDWR : O_RDONLY);
+ fd = open(dev, mode);
+ if (fd < 0 && !silent) {
+ perror(dev);
+ if (rw)
+ errx(EXIT_FAILURE, _("cannot open %s read-write"), dev);
+ else
+ errx(EXIT_FAILURE, _("cannot open %s for reading"), dev);
+ }
+ return fd;
+}
+
+static void
+do_list(char *dev, int silent) {
+ int fd;
+ struct disk_desc *z;
+
+ fd = my_open(dev, 0, silent);
+ if (fd < 0)
+ return;
+
+ z = &oldp;
+
+ free_sectors();
+ get_cylindersize(dev, fd, dump ? 1 : opt_list ? 0 : 1);
+ get_partitions(dev, fd, z);
+
+ if (opt_list)
+ out_partitions(dev, z);
+
+ if (verify) {
+ if (partitions_ok(fd, z))
+ my_warn(_("%s: OK\n"), dev);
+ else
+ exit_status = 1;
+ }
+
+ close(fd);
+}
+
+static void
+do_geom(char *dev, int silent) {
+ int fd;
+ struct geometry R;
+
+ fd = my_open(dev, 0, silent);
+ if (fd < 0)
+ return;
+
+ R = get_geometry(dev, fd, silent);
+ if (R.cylinders)
+ printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"),
+ dev, R.cylinders, R.heads, R.sectors);
+
+ close(fd);
+}
+
+static void
+do_pt_geom(char *dev, int silent) {
+ int fd;
+ struct disk_desc *z;
+ struct geometry R;
+
+ fd = my_open(dev, 0, silent);
+ if (fd < 0)
+ return;
+
+ z = &oldp;
+
+ free_sectors();
+ get_cylindersize(dev, fd, 1);
+ get_partitions(dev, fd, z);
+
+ R = B;
+
+ if (z->partno != 0 && get_fdisk_geometry(z)) {
+ R.heads = F.heads;
+ R.sectors = F.sectors;
+ R.cylindersize = R.heads * R.sectors;
+ R.cylinders = (R.cylindersize == 0) ? 0 : R.total_size / R.cylindersize;
+ }
+
+ if (R.cylinders)
+ printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"),
+ dev, R.cylinders, R.heads, R.sectors);
+
+ close(fd);
+}
+
+/* for compatibility with earlier fdisk: provide option -s */
+static void
+do_size(char *dev, int silent) {
+ int fd;
+ unsigned long long size;
+
+ fd = my_open(dev, 0, silent);
+ if (fd < 0)
+ return;
+
+ if (blkdev_get_sectors(fd, &size) == -1) {
+ if (!silent) {
+ perror(dev);
+ errx(EXIT_FAILURE, _("Cannot get size of %s"), dev);
+ }
+ goto done;
+ }
+
+ size /= 2; /* convert sectors to blocks */
+
+ /* a CDROM drive without mounted CD yields MAXINT */
+ if (silent && size == ((1 << 30) - 1))
+ goto done;
+
+ if (silent)
+ printf("%s: %9llu\n", dev, size);
+ else
+ printf("%llu\n", size);
+
+ total_size += size;
+
+done:
+ close(fd);
+}
+
+/*
+ * Activate: usually one wants to have a single primary partition
+ * to be active. OS/2 fdisk makes non-bootable logical partitions
+ * active - I don't know what that means to OS/2 Boot Manager.
+ *
+ * Call: activate /dev/hda 2 5 7 make these partitions active
+ * and the remaining ones inactive
+ * Or: sfdisk -A /dev/hda 2 5 7
+ *
+ * If only a single partition must be active, one may also use the form
+ * sfdisk -A2 /dev/hda
+ *
+ * With "activate /dev/hda" or "sfdisk -A /dev/hda" the active partitions
+ * are listed but not changed. To get zero active partitions, use
+ * "activate /dev/hda none" or "sfdisk -A /dev/hda none".
+ * Use something like `echo ",,,*" | sfdisk -N2 /dev/hda' to only make
+ * /dev/hda2 active, without changing other partitions.
+ *
+ * A warning will be given if after the change not precisely one primary
+ * partition is active.
+ *
+ * The present syntax was chosen to be (somewhat) compatible with the
+ * activate from the LILO package.
+ */
+static void
+set_active(struct disk_desc *z, char *pnam) {
+ int pno;
+
+ pno = asc_to_index(pnam, z);
+ if (z->partitions[pno].ptype == DOS_TYPE)
+ z->partitions[pno].p.bootable = 0x80;
+}
+
+static void
+do_activate(char **av, int ac, char *arg) {
+ char *dev = av[0];
+ int fd;
+ int rw, i, pno, lpno;
+ struct disk_desc *z;
+
+ z = &oldp;
+
+ rw = (!no_write && (arg || ac > 1));
+ fd = my_open(dev, rw, 0);
+
+ free_sectors();
+ get_cylindersize(dev, fd, 1);
+ get_partitions(dev, fd, z);
+
+ if (!arg && ac == 1) {
+ /* list active partitions */
+ for (pno = 0; pno < z->partno; pno++) {
+ if (z->partitions[pno].p.bootable) {
+ lpno = index_to_linux(pno, z);
+ if (pno == linux_to_index(lpno, z))
+ printf("%s\n", partname(dev, lpno, 0));
+ else
+ printf("%s#%d\n", dev, pno);
+ if (z->partitions[pno].p.bootable != 0x80)
+ my_warn(_("bad active byte: 0x%x instead of 0x80\n"),
+ z->partitions[pno].p.bootable);
+ }
+ }
+ } else {
+ /* clear `active byte' everywhere */
+ for (pno = 0; pno < z->partno; pno++)
+ if (z->partitions[pno].ptype == DOS_TYPE)
+ z->partitions[pno].p.bootable = 0;
+
+ /* then set where desired */
+ if (ac == 1)
+ set_active(z, arg);
+ else
+ for (i = 1; i < ac; i++)
+ set_active(z, av[i]);
+
+ /* then write to disk */
+ if (write_partitions(dev, fd, z))
+ my_warn(_("Done\n\n"));
+ else
+ exit_status = 1;
+ }
+ i = 0;
+ for (pno = 0; pno < z->partno && pno < 4; pno++)
+ if (z->partitions[pno].p.bootable)
+ i++;
+ if (i != 1)
+ my_warn(_("You have %d active primary partitions. This does not matter for LILO,\n"
+ "but the DOS MBR will only boot a disk with 1 active partition.\n"),
+ i);
+
+ close(fd);
+}
+
+static void
+set_unhidden(struct disk_desc *z, char *pnam) {
+ int pno;
+ unsigned char id;
+
+ pno = asc_to_index(pnam, z);
+ id = z->partitions[pno].p.sys_type;
+ if (id == 0x11 || id == 0x14 || id == 0x16 || id == 0x17)
+ id -= 0x10;
+ else
+ errx(EXIT_FAILURE, _("partition %s has id %x and is not hidden"), pnam, id);
+ z->partitions[pno].p.sys_type = id;
+}
+
+/*
+ * maybe remove and make part of --change-id
+ */
+static void
+do_unhide(char **av, int ac, char *arg) {
+ char *dev = av[0];
+ int fd, rw, i;
+ struct disk_desc *z;
+
+ z = &oldp;
+
+ rw = !no_write;
+ fd = my_open(dev, rw, 0);
+
+ free_sectors();
+ get_cylindersize(dev, fd, 1);
+ get_partitions(dev, fd, z);
+
+ /* unhide where desired */
+ if (ac == 1)
+ set_unhidden(z, arg);
+ else
+ for (i = 1; i < ac; i++)
+ set_unhidden(z, av[i]);
+
+ /* then write to disk */
+ if (write_partitions(dev, fd, z))
+ my_warn(_("Done\n\n"));
+ else
+ exit_status = 1;
+
+ close(fd);
+}
+
+static void
+do_change_id(char *dev, char *pnam, char *id) {
+ int fd, rw, pno;
+ struct disk_desc *z;
+ unsigned long i;
+
+ z = &oldp;
+
+ rw = !no_write;
+ fd = my_open(dev, rw, 0);
+
+ free_sectors();
+ get_cylindersize(dev, fd, 1);
+ get_partitions(dev, fd, z);
+
+ pno = asc_to_index(pnam, z);
+ if (id == 0) {
+ printf("%x\n", z->partitions[pno].p.sys_type);
+ goto done;
+ }
+ i = strtoul(id, NULL, 16);
+ if (i > 255)
+ errx(EXIT_FAILURE, _("Bad Id %lx"), i);
+ z->partitions[pno].p.sys_type = i;
+
+ if (write_partitions(dev, fd, z))
+ my_warn(_("Done\n\n"));
+ else
+ exit_status = 1;
+
+done:
+ close(fd);
+}
+
+static void
+do_reread(char *dev) {
+ int fd;
+
+ fd = my_open(dev, 0, 0);
+ if (reread_ioctl(fd)) {
+ warnx(_("This disk is currently in use.\n"));
+ exit(1);
+ }
+
+ close(fd);
+}
+
+/*
+ * I. Writing the new situation
+ */
+
+static void
+do_fdisk(char *dev) {
+ int fd;
+ char answer[32];
+ struct stat statbuf;
+ int interactive = isatty(0);
+ struct disk_desc *z;
+
+ if (stat(dev, &statbuf) < 0) {
+ perror(dev);
+ errx(EXIT_FAILURE, _("Fatal error: cannot find %s"), dev);
+ }
+ if (!S_ISBLK(statbuf.st_mode)) {
+ warnx(_("Warning: %s is not a block device\n"), dev);
+ no_reread = 1;
+ }
+ fd = my_open(dev, !no_write, 0);
+
+ if (!no_write && !no_reread) {
+ my_warn(_("Checking that no-one is using this disk right now ...\n"));
+ if (reread_ioctl(fd)) {
+ warnx(_("\nThis disk is currently in use - repartitioning is probably a bad idea.\n"
+ "Umount all file systems, and swapoff all swap partitions on this disk.\n"
+ "Use the --no-reread flag to suppress this check.\n"));
+ if (!force) {
+ warnx(_("Use the --force flag to overrule all checks.\n"));
+ exit(1);
+ }
+ } else
+ my_warn(_("OK\n"));
+ }
+
+ z = &oldp;
+
+ free_sectors();
+ get_cylindersize(dev, fd, 0);
+ get_partitions(dev, fd, z);
+
+ printf(_("Old situation:\n"));
+ out_partitions(dev, z);
+
+ if (one_only && (one_only_pno = linux_to_index(one_only, z)) < 0)
+ errx(EXIT_FAILURE, _("Partition %d does not exist, cannot change it"), one_only);
+
+ z = &newp;
+
+ while (1) {
+
+ read_input(dev, interactive, z);
+
+ printf(_("New situation:\n"));
+ out_partitions(dev, z);
+
+ if (!partitions_ok(fd, z) && !force) {
+ if (!interactive)
+ errx(EXIT_FAILURE, _("I don't like these partitions - nothing changed.\n"
+ "(If you really want this, use the --force option.)"));
+ else
+ warnx(_("I don't like this - probably you should answer No\n"));
+ }
+ if (interactive) {
+ ask:
+ if (no_write)
+ /* TRANSLATORS: sfdisk uses rpmatch which means the answers y and n
+ * should be translated, but that is not the case with q answer. */
+ printf(_("Are you satisfied with this? [ynq] "));
+ else
+ printf(_("Do you want to write this to disk? [ynq] "));
+ ignore_result( fgets(answer, sizeof(answer), stdin) );
+ if (answer[0] == 'q' || answer[0] == 'Q') {
+ errx(EXIT_FAILURE, _("Quitting - nothing changed"));
+ } else if (rpmatch(answer) == 1) {
+ continue;
+ } else if (rpmatch(answer) == 0) {
+ break;
+ } else {
+ printf(_("Please answer one of y,n,q\n"));
+ goto ask;
+ }
+ } else
+ break;
+ }
+
+ if (write_partitions(dev, fd, z))
+ printf(_("Successfully wrote the new partition table\n\n"));
+ else
+ exit_status = 1;
+
+ if (!reread_disk_partition(dev, fd)) { /* close fd on success */
+ close(fd);
+ exit_status = 1;
+ }
+ my_warn(_("If you created or changed a DOS partition, /dev/foo7, say, then use dd(1)\n"
+ "to zero the first 512 bytes: dd if=/dev/zero of=/dev/foo7 bs=512 count=1\n"
+ "(See fdisk(8).)\n"));
+
+ sync(); /* superstition */
+ exit(exit_status);
+}
diff --git a/fdisks/utils.c b/fdisks/utils.c
new file mode 100644
index 0000000..85e09c5
--- /dev/null
+++ b/fdisks/utils.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_LIBBLKID
+#include <blkid.h>
+#endif
+
+#include "nls.h"
+#include "blkdev.h"
+#include "common.h"
+#include "fdisk.h"
+
+#include "fdiskdoslabel.h"
+#include "fdisksunlabel.h"
+
+int fdisk_debug_mask;
+
+/*
+ * Label probing functions.
+ */
+static const struct fdisk_label *labels[] =
+{
+ &dos_label,
+ &sun_label,
+ &sgi_label,
+ &aix_label,
+ &bsd_label,
+ &mac_label,
+};
+
+/**
+ * fdisk_write_disklabel
+ * @cxt: fdisk context
+ *
+ * Write in-memory changes to disk
+ *
+ * Returns 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_write_disklabel(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->write)
+ return -ENOSYS;
+
+ return cxt->label->write(cxt);
+}
+
+/**
+ * fdisk_verify_disklabel:
+ * @cxt: fdisk context
+ *
+ * Verifies the partition tabe.
+ *
+ * Returns 0.
+ */
+int fdisk_verify_disklabel(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->verify)
+ return -ENOSYS;
+
+ return cxt->label->verify(cxt);
+}
+
+/**
+ * fdisk_add_partition:
+ * @cxt: fdisk context
+ * @partnum: partition number to create
+ * @parttype: partition type to create
+ *
+ * Creates a new partition, with number @partnum and type @parttype.
+ *
+ * Returns 0.
+ */
+int fdisk_add_partition(struct fdisk_context *cxt, int partnum, int parttype)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->part_add)
+ return -ENOSYS;
+
+ cxt->label->part_add(cxt, partnum, parttype);
+ return 0;
+}
+
+/**
+ * fdisk_delete_partition:
+ * @cxt: fdisk context
+ * @partnum: partition number to delete
+ *
+ * Deletes a @partnum partition.
+ *
+ * Returns 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_delete_partition(struct fdisk_context *cxt, int partnum)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->part_delete)
+ return -ENOSYS;
+
+ DBG(LABEL, dbgprint("deleting %s partition number %d",
+ cxt->label->name, partnum));
+ cxt->label->part_delete(cxt, partnum);
+ return 0;
+}
+
+static int __probe_labels(struct fdisk_context *cxt)
+{
+ size_t i;
+
+ disklabel = ANY_LABEL;
+ update_units(cxt);
+
+ for (i = 0; i < ARRAY_SIZE(labels); i++) {
+ if (!labels[i]->probe || labels[i]->probe(cxt) != 1)
+ continue;
+
+ cxt->label = labels[i];
+
+ DBG(LABEL, dbgprint("detected a %s label", cxt->label->name));
+ return 0;
+ }
+
+ return 1; /* not found */
+}
+
+static int __init_firstsector_buffer(struct fdisk_context *cxt)
+{
+ DBG(TOPOLOGY, dbgprint("initialize first sector buffer"));
+
+ cxt->firstsector = calloc(1, MAX_SECTOR_SIZE);
+ if (!cxt->firstsector)
+ goto fail;
+
+ /* read MBR */
+ if (512 != read(cxt->dev_fd, cxt->firstsector, 512)) {
+ if (errno == 0)
+ errno = EINVAL; /* probably too small file/device */
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -errno;
+}
+
+static unsigned long __get_sector_size(int fd)
+{
+ int sect_sz;
+
+ if (!blkdev_get_sector_size(fd, &sect_sz))
+ return (unsigned long) sect_sz;
+ return DEFAULT_SECTOR_SIZE;
+}
+
+/**
+ * fdisk_context_force_sector_size:
+ * @cxt: fdisk context
+ * @s: required sector size
+ *
+ * Overwrites logical and physical sector size. Note that the default sector
+ * size is discovered by fdisk_new_context_from_device() from device topology.
+ *
+ * Don't use this function, rely on the default behavioer is more safe.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_context_force_sector_size(struct fdisk_context *cxt, sector_t s)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->phy_sector_size = cxt->sector_size = s;
+ cxt->min_io_size = cxt->io_size = s;
+
+ update_sector_offset(cxt);
+ return 0;
+}
+
+static void recount_geometry(struct fdisk_context *cxt)
+{
+ cxt->geom.cylinders = cxt->total_sectors /
+ (cxt->geom.heads * cxt->geom.sectors);
+}
+
+/**
+ * fdisk_context_set_user_geometry:
+ * @cxt: fdisk context
+ * @cylinders: user specified cylinders
+ * @heads: user specified heads
+ * @sectors: user specified sectors
+ *
+ * Overrides autodiscovery and apply user specified geometry.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_context_set_user_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (heads)
+ cxt->geom.heads = heads;
+ if (sectors)
+ cxt->geom.sectors = sectors;
+
+ if (cylinders)
+ cxt->geom.cylinders = cylinders;
+ else
+ recount_geometry(cxt);
+
+ update_sector_offset(cxt);
+ return 0;
+}
+
+/*
+ * Generic (label independent) geometry
+ */
+static int __discover_system_geometry(struct fdisk_context *cxt)
+{
+ sector_t nsects;
+ unsigned int h = 0, s = 0;
+
+ /* get number of 512-byte sectors, and convert it the real sectors */
+ if (!blkdev_get_sectors(cxt->dev_fd, &nsects))
+ cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
+
+ /* what the kernel/bios thinks the geometry is */
+ blkdev_get_geometry(cxt->dev_fd, &h, &s);
+ if (!h && !s) {
+ /* unable to discover geometry, use default values */
+ s = 63;
+ h = 255;
+ }
+
+ /* obtained heads and sectors */
+ cxt->geom.heads = h;
+ cxt->geom.sectors = s;
+ recount_geometry(cxt);
+
+ DBG(GEOMETRY, dbgprint("geometry discovered for %s: C/H/S: %lld/%d/%lld",
+ cxt->dev_path, cxt->geom.cylinders,
+ cxt->geom.heads, cxt->geom.sectors));
+ return 0;
+}
+
+static int __discover_topology(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+ blkid_probe pr;
+
+ DBG(TOPOLOGY, dbgprint("initialize libblkid prober"));
+
+ pr = blkid_new_probe();
+ if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
+ blkid_topology tp = blkid_probe_get_topology(pr);
+
+ if (tp) {
+ cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
+ cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
+ cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
+ cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
+
+ /* I/O size used by fdisk */
+ cxt->io_size = cxt->optimal_io_size;
+ if (!cxt->io_size)
+ /* optimal IO is optional, default to minimum IO */
+ cxt->io_size = cxt->min_io_size;
+ }
+ }
+ blkid_free_probe(pr);
+#endif
+
+ /* no blkid or error, use default values */
+ if (!cxt->min_io_size)
+ cxt->min_io_size = DEFAULT_SECTOR_SIZE;
+ if (!cxt->io_size)
+ cxt->io_size = DEFAULT_SECTOR_SIZE;
+
+ cxt->sector_size = __get_sector_size(cxt->dev_fd);
+ if (!cxt->phy_sector_size) /* could not discover physical size */
+ cxt->phy_sector_size = cxt->sector_size;
+
+ DBG(TOPOLOGY, dbgprint("topology discovered for %s:\n"
+ "\tlogical/physical sector sizes: %ld/%ld\n"
+ "\tfdisk/minimal/optimal io sizes: %ld/%ld/%ld\n",
+ cxt->dev_path, cxt->sector_size, cxt->phy_sector_size,
+ cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
+ return 0;
+}
+
+/**
+ * fdisk_zeroize_firstsector:
+ * @cxt: fdisk context
+ *
+ * Zeros in-memory first sector buffer
+ */
+void fdisk_zeroize_firstsector(struct fdisk_context *cxt)
+{
+ if (!cxt)
+ return;
+
+ if (cxt->firstsector) {
+ DBG(CONTEXT, dbgprint("zeroize in-memory first sector buffer"));
+ memset(cxt->firstsector, 0, MAX_SECTOR_SIZE);
+ }
+}
+
+/**
+ * fdisk_dev_sectsz_is_default:
+ * @cxt: fdisk context
+ *
+ * Returns 1 if the device's sector size is the default value, otherwise 0.
+ */
+int fdisk_dev_sectsz_is_default(struct fdisk_context *cxt)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ return cxt->sector_size == DEFAULT_SECTOR_SIZE;
+}
+
+/**
+ * fdisk_dev_has_topology:
+ * @cxt: fdisk context
+ *
+ * Returns 1 if the device provides topology information, otherwise 0.
+ */
+int fdisk_dev_has_topology(struct fdisk_context *cxt)
+{
+ /*
+ * Assume that the device provides topology info if
+ * optimal_io_size is set or alignment_offset is set or
+ * minimum_io_size is not power of 2.
+ */
+ if (cxt &&
+ (cxt->optimal_io_size ||
+ cxt->alignment_offset ||
+ !is_power_of_2(cxt->min_io_size)))
+ return 1;
+ return 0;
+}
+
+/**
+ * fdisk_dev_has_disklabel:
+ * @cxt: fdisk context
+ *
+ * Returns: return 1 if there is label on the device.
+ */
+int fdisk_dev_has_disklabel(struct fdisk_context *cxt)
+{
+ return cxt && disklabel != ANY_LABEL;
+}
+
+/**
+ * fdisk_create_disklabel:
+ * @cxt: fdisk context
+ * @name: label name
+ *
+ * Creates a new disk label of type @name. If @name is NULL, then it
+ * will create a default system label type, either SUN or DOS.
+ *
+ * Returns 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ cxt->label = NULL;
+
+ if (!name) { /* use default label creation */
+#ifdef __sparc__
+ cxt->label = &sun_label;
+#else
+ cxt->label = &dos_label;
+#endif
+ } else {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(labels); i++) {
+ if (strcmp(name, labels[i]->name) != 0)
+ continue;
+
+ cxt->label = labels[i];
+ DBG(LABEL, dbgprint("changing to %s label\n", cxt->label->name));
+ break;
+ }
+ }
+
+ if (!cxt->label)
+ return -EINVAL;
+ if (!cxt->label->create)
+ return -ENOSYS;
+
+ return cxt->label->create(cxt);
+}
+
+/**
+ * fdisk_init_debug:
+ * @mask: debug mask (0xffff to enable full debuging)
+ *
+ * If the @mask is not specified then this function reads
+ * FDISK_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ */
+void fdisk_init_debug(int mask)
+{
+ if (fdisk_debug_mask & FDISK_DEBUG_INIT)
+ return;
+ if (!mask) {
+ char *str = getenv("FDISK_DEBUG");
+ if (str)
+ fdisk_debug_mask = strtoul(str, 0, 0);
+ } else
+ fdisk_debug_mask = mask;
+
+ if (fdisk_debug_mask)
+ fprintf(stderr, "fdisk: debug mask set to 0x%04x.\n",
+ fdisk_debug_mask);
+ fdisk_debug_mask |= FDISK_DEBUG_INIT;
+}
+
+/**
+ * fdisk_new_context:
+ * @filename: path to the device to be handled
+ * @readonly: how to open the device
+ *
+ * If the @readonly flag is set to false, fdisk will attempt to open
+ * the device with read-write mode and will fallback to read-only if
+ * unsuccessful.
+ *
+ * Returns: newly allocated fdisk context
+ */
+struct fdisk_context *fdisk_new_context_from_filename(const char *fname, int readonly)
+{
+ int fd, errsv = 0;
+ struct fdisk_context *cxt = NULL;
+
+ DBG(CONTEXT, dbgprint("initializing context for %s", fname));
+
+ if (readonly == 1 || (fd = open(fname, O_RDWR)) < 0) {
+ if ((fd = open(fname, O_RDONLY)) < 0)
+ return NULL;
+ readonly = 1;
+ }
+
+ cxt = calloc(1, sizeof(*cxt));
+ if (!cxt)
+ goto fail;
+
+ cxt->dev_fd = fd;
+ cxt->dev_path = strdup(fname);
+ if (!cxt->dev_path)
+ goto fail;
+
+ if (__init_firstsector_buffer(cxt) < 0)
+ goto fail;
+
+ __discover_topology(cxt);
+ __discover_system_geometry(cxt);
+
+ /* detect labels and apply labes specific stuff (e.g geomery)
+ * to the context */
+ __probe_labels(cxt);
+
+ update_sector_offset(cxt);
+
+ DBG(CONTEXT, dbgprint("context initialized for %s [%s]",
+ fname, readonly ? "READ-ONLY" : "READ-WRITE"));
+ return cxt;
+fail:
+ errsv = errno;
+ fdisk_free_context(cxt);
+ errno = errsv;
+
+ DBG(CONTEXT, dbgprint("failed to initialize context for %s: %m", fname));
+ return NULL;
+}
+
+/**
+ * fdisk_free_context:
+ * @cxt: fdisk context
+ *
+ * Deallocates context struct.
+ */
+void fdisk_free_context(struct fdisk_context *cxt)
+{
+ if (!cxt)
+ return;
+
+ DBG(CONTEXT, dbgprint("freeing context for %s", cxt->dev_path));
+ close(cxt->dev_fd);
+ free(cxt->dev_path);
+ free(cxt->firstsector);
+ free(cxt);
+}