diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-11-02 20:15:39 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-11-02 20:15:39 +0400 |
commit | b13154de3eca5ba28fbb4854d916cd0be5febeed (patch) | |
tree | 30f2e9e89ab71a2df837076ac68c3ba770230294 /fdisks | |
download | util-linux-upstream.tar.gz |
Imported Upstream version 2.22upstream/2.22upstream
Diffstat (limited to 'fdisks')
-rw-r--r-- | fdisks/Makemodule.am | 87 | ||||
-rw-r--r-- | fdisks/cfdisk.8 | 452 | ||||
-rw-r--r-- | fdisks/cfdisk.c | 2854 | ||||
-rw-r--r-- | fdisks/common.h | 15 | ||||
-rw-r--r-- | fdisks/fdisk.8 | 288 | ||||
-rw-r--r-- | fdisks/fdisk.c | 1935 | ||||
-rw-r--r-- | fdisks/fdisk.h | 294 | ||||
-rw-r--r-- | fdisks/fdiskaixlabel.c | 90 | ||||
-rw-r--r-- | fdisks/fdiskaixlabel.h | 25 | ||||
-rw-r--r-- | fdisks/fdiskbsdlabel.c | 855 | ||||
-rw-r--r-- | fdisks/fdiskbsdlabel.h | 245 | ||||
-rw-r--r-- | fdisks/fdiskdoslabel.c | 833 | ||||
-rw-r--r-- | fdisks/fdiskdoslabel.h | 47 | ||||
-rw-r--r-- | fdisks/fdiskmaclabel.c | 108 | ||||
-rw-r--r-- | fdisks/fdiskmaclabel.h | 34 | ||||
-rw-r--r-- | fdisks/fdisksgilabel.c | 907 | ||||
-rw-r--r-- | fdisks/fdisksgilabel.h | 131 | ||||
-rw-r--r-- | fdisks/fdisksunlabel.c | 661 | ||||
-rw-r--r-- | fdisks/fdisksunlabel.h | 91 | ||||
-rw-r--r-- | fdisks/gpt.c | 216 | ||||
-rw-r--r-- | fdisks/gpt.h | 7 | ||||
-rw-r--r-- | fdisks/i386_sys_types.c | 110 | ||||
-rw-r--r-- | fdisks/partname.c | 48 | ||||
-rw-r--r-- | fdisks/sfdisk.8 | 603 | ||||
-rw-r--r-- | fdisks/sfdisk.c | 3242 | ||||
-rw-r--r-- | fdisks/utils.c | 523 |
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, §or_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, §ors) == -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, §or_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, §_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); +} |