diff options
author | Karel Zak | 2006-12-07 00:25:37 +0100 |
---|---|---|
committer | Karel Zak | 2006-12-07 00:25:37 +0100 |
commit | 5c36a0eb7cdb0360f9afd5d747c321f423b35984 (patch) | |
tree | 147599a77eaff2b5fbc0d389e89d2b51602326c0 /fdisk | |
parent | Imported from util-linux-2.8 tarball. (diff) | |
download | kernel-qcow2-util-linux-5c36a0eb7cdb0360f9afd5d747c321f423b35984.tar.gz kernel-qcow2-util-linux-5c36a0eb7cdb0360f9afd5d747c321f423b35984.tar.xz kernel-qcow2-util-linux-5c36a0eb7cdb0360f9afd5d747c321f423b35984.zip |
Imported from util-linux-2.9i tarball.
Diffstat (limited to 'fdisk')
-rw-r--r-- | fdisk/Makefile | 68 | ||||
-rw-r--r-- | fdisk/README.cfdisk | 45 | ||||
-rw-r--r-- | fdisk/README.fdisk | 583 | ||||
-rw-r--r-- | fdisk/cfdisk.8 | 406 | ||||
-rw-r--r-- | fdisk/cfdisk.c | 2642 | ||||
-rw-r--r-- | fdisk/fdisk.8 | 223 | ||||
-rw-r--r-- | fdisk/fdisk.c | 2046 | ||||
-rw-r--r-- | fdisk/fdisk.h | 85 | ||||
-rw-r--r-- | fdisk/fdiskaixlabel.c | 64 | ||||
-rw-r--r-- | fdisk/fdiskaixlabel.h | 34 | ||||
-rw-r--r-- | fdisk/fdiskbsdlabel.c | 825 | ||||
-rw-r--r-- | fdisk/fdiskbsdlabel.h | 229 | ||||
-rw-r--r-- | fdisk/fdisksgilabel.c | 888 | ||||
-rw-r--r-- | fdisk/fdisksgilabel.h | 133 | ||||
-rw-r--r-- | fdisk/fdisksunlabel.c | 690 | ||||
-rw-r--r-- | fdisk/fdisksunlabel.h | 74 | ||||
-rw-r--r-- | fdisk/llseek.c | 102 | ||||
-rw-r--r-- | fdisk/sfdisk.8 | 506 | ||||
-rw-r--r-- | fdisk/sfdisk.c | 2864 | ||||
-rw-r--r-- | fdisk/sfdisk.examples | 264 |
20 files changed, 12771 insertions, 0 deletions
diff --git a/fdisk/Makefile b/fdisk/Makefile new file mode 100644 index 000000000..e5415acdc --- /dev/null +++ b/fdisk/Makefile @@ -0,0 +1,68 @@ +# Makefile -- Makefile for util-linux Linux utilities +# Created: Sat Dec 26 20:09:40 1992 +# Revised: Fri Oct 6 21:02:21 1995 by r.faith@ieee.org +# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu) +# + +include ../MCONFIG + +MAN8= +SBIN= + +ifneq "$(CPU)" "sparc" +# fsck and mkfs will compile, but there is no kernel support on sparc +ifneq "$(CPU)" "m68k" +MAN8:=$(MAN8) fdisk.8 cfdisk.8 sfdisk.8 +SBIN:=$(SBIN) fdisk cfdisk sfdisk +endif +else +MAN8:=$(MAN8) fdisk.8 +SBIN:=$(SBIN) fdisk +endif + +all: $(SBIN) + +cfdisk.o: cfdisk.c +ifeq "$(HAVE_SLANG)" "yes" + $(CC) -c $(CFLAGS) -DSLCURSES=1 $< -o $@ +else +ifeq "$(HAVE_NCURSES)" "yes" + $(CC) -c $(CFLAGS) $< -o $@ +else + : +endif +endif + +cfdisk: cfdisk.o llseek.o +ifeq "$(HAVE_SLANG)" "yes" + $(CC) $(LDFLAGS) $^ -o $@ $(LIBSLANG) +else +ifeq "$(HAVE_NCURSES)" "yes" + $(CC) $(LDFLAGS) $^ -o $@ $(LIBCURSES) -lm +else + @echo $@ not made since it requires ncurses or slang +endif +endif + +# not installed by default +activate: sfdisk + rm -f activate + ln -s sfdisk activate + +fdisk: fdisk.o llseek.o fdiskbsdlabel.o fdisksgilabel.o fdisksunlabel.o \ + fdiskaixlabel.o +fdisk.o: fdisk.c fdisk.h +fdiskbsdlabel.o: fdiskbsdlabel.c fdisk.h fdiskbsdlabel.h +fdisksunlabel.o: fdisksunlabel.c fdisksunlabel.h fdisk.h +fdiskaixlabel.o: fdiskaixlabel.c fdiskaixlabel.h fdisk.h +sfdisk: sfdisk.o + +install: all + $(INSTALLDIR) $(SBINDIR) + $(INSTALLBIN) $(SBIN) $(SBINDIR) + $(INSTALLDIR) $(MAN8DIR) + $(INSTALLMAN) $(MAN8) $(MAN8DIR) + +.PHONY: clean +clean: + -rm -f *.o *~ core $(SBIN) diff --git a/fdisk/README.cfdisk b/fdisk/README.cfdisk new file mode 100644 index 000000000..5241ad136 --- /dev/null +++ b/fdisk/README.cfdisk @@ -0,0 +1,45 @@ +Announcing the new curses based fdisk program... 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). I hope that this program will be +useful to both new and old Linux users, and I hope it will make the +installation process easier. + + + **** WARNING **** +If you write a bad partition table to disk, it may destroy data and +partitions. + + +You can FTP cfdisk from ftp.cs.unc.edu in the /pub/martin/linux +directory. + +I would also like comments (good and bad) on the user interface, logic +and ease of use. If you have any suggestions for improvements, I +would be happy to hear them. + +My e-mail address is martin@cs.unc.edu. + +------------------------------------------------------------------- + + 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 cfdisk; if not, write to the Free Software Foundation, +Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +___ +Kevin E. Martin University of North Carolina at Chapel Hill +martin@cs.unc.edu Department of Computer Science diff --git a/fdisk/README.fdisk b/fdisk/README.fdisk new file mode 100644 index 000000000..86b182525 --- /dev/null +++ b/fdisk/README.fdisk @@ -0,0 +1,583 @@ +`fdisk': the Linux partition table editor +========================================= + +`fdisk' is the Linux partition table editor. In this section we +examine this utility and try to describe it thoroughly enough so that +anyone can use it. + +* Contents: + +* Disks and how they are described. +* Dividing up your disk. +* The `fdisk' command. +* Deleting and adding partitions. +* Active flags and system types. +* Extra commands for experts. +* Warnings for `fdisk' users. + + +Disks and how they are described +-------------------------------- + +A typical disk consists physically of one or more circular objects +called "platters", which rotate about a central axis. Devices called +"heads" move to specified places on the disk surface to read or write +information. There is usually one head on each side of every platter, +and all these heads are attached to a comb-like controller arm which +moves all of them at the same time, either closer to the centre of the +disk, or closer to the outer edge. + +Suppose the arm is in one position, putting an area of the disk +surface within reach of one or another of the heads. This total area, +everything that is accessible without moving the arm, is called a +"cylinder". (A cylinder is a barrel-shaped cross section of a disk, +consisting of a circular strip from each side of each platter.) The +part of a cylinder that one head can read or write without moving is +called a "track". + +Each track is divided into several pie-shaped slices called +"sectors", which are the smallest parts of the disk which can be read +or written at a time. The sectors on one disk are usually all the same +size. + +In fact, there are not always two heads to every platter, there are +some disks which do not have the same amount of data in every cylinder, +and there may be disks which do not have the same amount of data in +every sector. These features are usually hidden on PCs by the +controller card or the BIOS, which map the physical geometry of a disk +onto a logical geometry, which is what is actually used to access the +disk. + +The numbers which describe the "geometry" of a disk are + + 1. The number of cylinders it contains. + + 2. The number of tracks per cylinder, which is the number of heads. + + 3. The number of sectors per track. + + 4. The number of bytes per sector. + +These numbers vary from disk to disk, but a typical PC disk might +have about 1000 cylinders, half a dozen heads, and 15 or 20 sectors per +track, with each sector containing 512 bytes or characters; such a disk +contains 40 to 60 megabytes of data. A "double density" floppy disk +contains 40 cylinders, with 2 heads (2 tracks per cylinder), and with 9 +sectors per track; such a disk contains 360 kilobytes, or 360 * 1024 +characters. A "high density" 3.5 inch floppy contains 80 cylinders, +with 2 heads and 18 sectors per track, or 1.44 megabytes, or 1440 * +1024 characters. + +The exact size of a track or cylinder in bytes varies from one disk +to another. This `fdisk' for Linux deals mainly with cylinders, since +this is the best unit to use when allocating space for partitions. It +reports partition sizes in "blocks" of 1024 bytes, or 2 sectors, since +`mkswap' and the various `mkfs' programs require this number. A block +is the smallest amount of space which can be set aside for a file in +the current file systems. + +An operating system, such as Linux or DOS or OS/2, may use a disk in +any way that it wishes, but if two operating systems share the same +disk, they must agree on who owns what, or else one will interfere with +the other (that is, by damaging the other's files). A "partition" is a +section of a hard disk which is handled as a unit by all operating +systems which can access the disk. The standard way to define +partitions (for the moment) is the "partition table", a list of +information which is stored in parts of the disk that don't belong to +any of the systems using the disk. The beginning of the partition +table is stored in the disk's primary boot sector, and the rest is +stored in a chain of sectors scattered throughout the disk. + +The first sector on the disk is called the "primary boot block" or +"primary boot sector" because (1) it comes first, before other, similar +sectors; (2) it tells where the other, similar sectors are found, so +that it is logically `prior' to them; and (3) it usually contains code +which is executed when the system boots up. This sector contains a +table describing at most four partitions. These areas are called +"primary partitions". + +The partition table in the primary boot sector may also describe at +most one "extended partition". This is a large area of the disk, +usually containing all the space which is not in any primary partition. +Within this space we can set aside other areas which are called +"logical partitions", because they look almost exactly like primary +partitions. In fact, the main difference between them is that we can +boot from primary partitions, while we cannot boot from logical +partitions. This happens because the address of a primary partition is +in a fixed place, whereas the address of a secondary partition is not, +so we require a more complicated process to discover it, one which is +too difficult for most primary boot programs. + + +Dividing up your disk +--------------------- + +It is a good idea to plan ahead before you start creating partitions +on your disk. If you set aside a partition for some purpose, it is not +easy to change its size: you must backup all the data from the partition, +whether to floppies, to another partition, to another hard disk, or +somewhere else; then you must edit the table which describes this +partition, so changing its size; then you must reboot and initialise +the new partition, formatting it, for example, under DOS, or running +`mkfs' under Linux; finally you can copy all the data back. It is +possible, if you have several partitions, to copy data back and forth +between them while you change their sizes, but this is a bit risky and +time consuming. It is better to plan ahead what you will need, since +it is hard to change it afterwards. + +Many people with large disks and recent versions of DOS have their +entire file system on one large partition. They usually ask, `Isn't +there any way I can reformat my disk without copying everything off?' +There is no way to do it using standard DOS utilities, and there is no +truly safe way to do it using commercial software, because, if you make +a mistake, you will lose the entire contents of your disk. If you are +going to back up your disk anyway, you might as well copy the data back +safely. The Linux FAQ contains references to tools and procedures +which will allow you to do this, if you dare. + +DOS and Linux both allow you to access several partitions on a +single disk; on DOS these are treated as if they were separate disks or +drives, and under Linux they are treated as different "devices". + +You can have up to 64 partitions on a single IDE disk, or up to 16 +partitions on a single SCSI disk, at least as far as Linux is +concerned; in practice you will rarely want so many. The maximum size +of a Linux file system on a single partition depends on the type of +file system you use. Minix file systems are limited to 64 megabytes. +You may have all of your Linux files in a single partition, or you may +have two, three, or more Linux file systems. Similarly you may have +one or more DOS partitions. If you have several small partitions, you +run much less risk of losing all your files if your disk gets +corrupted. On the other hand, you may run out of space on a small +partition more easily. + +Under DOS, you must refer to each partition by a separate drive +letter, but all partitions are automatically accessible. Under Linux +only the root partition is automatically accessible, but once we mount +another partition, it is indistinguishable from the rest of the file +system. Disks are usually mounted by a command in one of the system +startup files, `/etc/rc', so you need not worry about having to do it +yourself whenever you boot the system. But even ordinary users may +be allowed to mount removable hard disks and floppy disks. + +Linux requires at least one partition, which is the `root' of the +file system. You may prefer to have a separate partition for `/usr', +which contains most of the executable files, or for `/home', which +contains most of your private files. You may also wish to set aside a +partition to use for swap space, depending on the amount of memory your +PC has. You will certainly need swap space if you have less than 4 Mb +of RAM and wish to compile anything substantial. You can reserve swap +space in a file, but you need a partition big enough to hold it, and +this will probably be less efficient than having a partition devoted to +swap. + +The disk space you need for Linux is discussed in README.prepare. + +Are you going to boot Linux from the hard disk, or will you boot +from a floppy? Some boot programs place severe restrictions on where +the boot partition can be. LILO is more relaxed about this, but does +require either the Master Boot Record on your first hard disk, or the +boot record on one of the first four partitions on your first hard disk. + +If you have an extended partition with logical partitions in it, you +can have only three primary partitions containing data. + + +The `fdisk' command +------------------- + +Every operating system, whether DOS, OS/2, or Linux, should provide +its own utility for editing hard disk partition tables. At least four +of these utilities have been called `fdisk', for `Fixed DISK setup +program', where `fixed' means `not removable'. I believe the first PC +program named `fdisk' came from Microsoft in about 1985; before that +time disks were too small to divide into separate sections. + +Every operating system has its own peculiarities. Normally you +should set up a partition for the use of one operating system by using +its own `fdisk' program. Do not use the Linux `fdisk' to create +partitions for DOS or for any system other than Linux; otherwise you +may have problems. + +An `fdisk' program performs two functions: it reports how the disk is +configured, and it changes that configuration by adding or deleting +partitions. Most `fdisk' programs can also change other information in +partition tables. + +This `fdisk' for Linux operates on one hard disk at a time. If you +give the command + + fdisk + +it reports on, and is able to change, `/dev/hda', the first hard +disk. (If you have no `/dev/hda', `fdisk' uses `/dev/sda' as the +default device.) To look at or change the second hard disk, `/dev/hdb', +give the command + + fdisk /dev/hdb + +To look at or change the first SCSI disk, give the command + + fdisk /dev/sda + +There are some special forms of the `fdisk' command. One of them, +suggested by Jim Winstead, simply lists all partitions on all available +disks: + + fdisk -l (where `l' is a letter, not the digit `1') + +The option `-v' is provided to list the current version of the +`fdisk' command. Finally, there is an option `-s' which is not really +intended for interactive use. It causes fdisk to print the size of a +partition in blocks of 1024 bytes as follows: + + fdisk -s /dev/hda7 + 39934 + +Because this is intended to be used by `mkfs' and `mkswap' programs, +it does not return the size of extended partitions or of partitions +whose system type code is less than 10 (hexadecimal a). If you start +`fdisk' without using one of these special options, it responds by +asking for a command: + + Command (m for help): _ + +Each `fdisk' command consists of a single letter, which must be +followed by <RETURN> before it is obeyed. Upper and lower case are not +distinguished. Anything you type after the first character is ignored. +Give the command `m', and you should see this menu: + Command action + a toggle a bootable flag + d delete a partition + l list known partition types + m print this menu + n add a new partition + p print the partition table + q quit without saving changes + t change a partition's system id + u change display/entry units + v verify the partition table + w write table to disk and exit + x extra functionality (experts only) + + Command (m for help): _ + +The simplest commands are Print, Verify, and Quit. On a small disk, the +Print command might produce a display like this one: + + Disk /dev/hda: 5 heads, 17 sectors, 977 cylinders + Units = cylinders of 85 * 512 bytes + + Device Boot Begin Start End Blocks Id System + /dev/hda1 * 1 1 236 10021+ 1 DOS 12-bit FAT + /dev/hda2 837 837 977 5992+ 5 Extended + /dev/hda3 * 237 237 836 25500 83 Linux native + /dev/hda5 837 837 936 4249+ 82 Linux swap + /dev/hda6 942 942 977 1522 1 DOS 12-bit FAT + +There are 5 partitions reported; `/dev/hda4' does not appear because +it is not allocated. Partitions 1 and 3 are flagged as bootable. The +size of each partition is reported in 1 kilobyte blocks; hence the +primary Linux partition, partition 3, is 25 1/2 megabytes in size. The +`+' after three of the sizes warns that these partitions contain an odd +number of sectors: Linux normally allocates filespace in 1 kilobyte +blocks, so the extra sector in partition 5 is wasted. Id numbers are +reported in hexadecimal and explained in English. + +The display/entry units may be either cylinders or sectors. The +default is cylinders, but changing the units makes the print command +display the following table for the system reported above: + + Disk /dev/hda: 5 heads, 17 sectors, 977 cylinders + Units = sectors of 1 * 512 bytes + + Device Boot Begin Start End Blocks Id System + /dev/hda1 * 1 17 20059 10021+ 1 DOS 12-bit FAT + /dev/hda2 71060 71060 83044 5992+ 5 Extended + /dev/hda3 * 20060 20060 71059 25500 83 Linux native + /dev/hda5 71061 71061 79559 4249+ 82 Linux swap + /dev/hda6 79985 80001 83044 1522 1 DOS 12-bit FAT + +The start of data in both DOS partitions is 16 sectors after the +beginning of the partition: this is one reason why you should use DOS's +own `FDISK' to create DOS partitions. Changing the units to sectors +also affects the way in which the new partition command asks for the +beginning and end of a new partition. + +*Warning*: it is dangerous to create a new partition when the +display/entry units are sectors. + +The Verify command is useful because + + 1. It warns you if anything is wrong. *Always* do a Verify command + to check your work before writing any changes to disk. + + 2. It reports how many unallocated sectors there are on the disk. + +The Quit command is also useful. `fdisk' does not actually change +any data on your disk unless you give a Write command. If you are +unhappy about any changes you may have made, give the Quit command, and +your disk will remain as it was before you ran `fdisk'. You can also +interrupt `fdisk' with `CTRL-C'. + + +Deleting and adding partitions +------------------------------ + +Deleting a partition is simple. Give the Delete command by typing +`d'. `fdisk' asks: + + Partition number (1-6): _ + +Once you get this far, you must either delete a partition or +interrupt the program with `CTRL-C' (or whatever your current interrupt +character is). Note: + + 1. You may delete a nonexistent partition. You will get a warning + message. + + 2. You may delete an extended partition. This has the side effect of + deleting all partitions greater than or equal to 5. + + 3. You may delete a logical partition. In that case, all partitions + above it are renumbered at once. For example, if you delete + partition 5, then partition 6 becomes known as partition 5, and + partition 7 as partition 6. + +Adding a partition is just a bit more complicated. Give the New +command by typing `n'. `fdisk' allows you to + + 1. Create a primary partition, if there is a free slot in the primary + partition table. + + 2. Create an extended partition if there is a free slot in the + primary partition table, and if there is no extended partition. + + 3. Create a logical partition if an extended partition exists. + +If more than one of these actions is possible, you will be asked to +select Primary, Extended, or Logical, depending on what is currently +permissible. Before you create a primary or an extended partition, you +are asked what slot it is to have in the table (1-4). + +You may not add a primary or an extended partition if the selected +slot in the primary partition table is already occupied: in that case +you simply return to the main menu. You are not allowed to add a new +primary partition unless there are sectors available outside the +extended partition. You are not allowed to add a new logical partition +unless there are sectors available inside the extended partition. + +If space is available, you are prompted for the first cylinder: + + First sector ([237]-977): _ + +The limits are the lowest and the highest cylinders in which sectors +are available in the appropriate part of the disk. The square-bracketed +number is what you'll get if you simply press enter. Not all numbers in +this range are necessarily available: they may fall inside an existing +partition. If you select a cylinder which is already in use, you are +told off and prompted again for the first cylinder. After selecting the +first cylinder, you are prompted again: + + Last cylinder or +size or +sizeM or +sizeK (237-[836]): _ + +The limits are the cylinder you have chosen as the first cylinder, +and the highest cylinder which contains a legitimate upper boundary for +the new partition. The square-bracketed number is what you'll get if +you simply press enter. In other words, all numbers in the given range are +legitimate, unlike those in the first range of cylinders. You may also +specify the size of a partition in megabytes, kilobytes, or in the +current units (cylinders or sectors). A plus sign `+' indicates that +your answer is a size rather than a boundary, and the suffix `m' or `k' +(upper or lower case) indicates that the size is not given in units of +sectors or cyliners, but in megabytes or kilobytes respectively. Thus +possible answers to the last cylinder request above are + +700 + Make cylinder 700 the last cylinder in the partition. + ++300 + Make cylinder 237 + 300 = 537 the last cylinder in the partition. + ++15m + Make the partition at least 15 megabytes in size. + ++12500k + Make the partition at least 12,500 kilobytes in size. + +If you specify a size which is too large or an end which is out of +range, fdisk complains and repeats the prompt. + +Adding or deleting partitions has no effect unless you subsequently +give the Write command. Please remember to give the Verify command +first, just before giving the Write command: this is a safety +precaution. After giving the Write command, you will see this message: + + The partition table has been altered! + Calling ioctl() to re-read partition table. + Syncing disks. + +If there are no further messages, the kernel has successfully copied +the information from the partition table into its own internal table. +But sometimes you will see a message like this one: + + Re-read table failed with error 16: Device or resource busy. + Reboot your system to ensure the partition table is updated. + +In this case, depending on what you have changed in the partition +table, it may be dangerous to continue working without rebooting, +since you may lose or corrupt your data. + + +Here are some important things to note: + + 1. Before you reboot, you *may* run `fdisk' again, either to manage + another disk, or to make additional changes to the same disk, or + just to check that the changes have been made as you expected. + This is true even after you receive the message warning you to + reboot. + + 2. It is not a good idea to run any of the programs `mkfs', `mkswap', + `mount', or `swapon' if you have received the warning message but + have not rebooted. In this case it is dangerous to run any program, + but these in particular may cause serious damage to the data on your + disk, including the partition tables themselves. + + +Active flags and system types +----------------------------- + +The active flag is a bit in the partition table entry which marks a +partition as bootable. This is important to some primary boot sector +programs, which will not boot from an unflagged partition. Other such +programs do not allow more than one partition to be flagged. Some, +like LILO, ignore the flags completely. I prefer to flag all bootable +partitions as active so that they stand out on the menu which `fdisk' +lists. Fdisk prints a star after the name of a partition's device file +if its active flag is set. + +The Active command changes, or toggles, a partition's active flag. +Give the Active command, and select a partition by number. If it was +marked inactive, it will be flagged as active; if it was flagged as +active, it will be marked inactive. You may set the active flag on an +extended or logical partition, though the meaning of such a flag is by +no means clear. This can be used to install LILO as a secondary boot +loader to boot a Linux which lives on a second hard disk. + +The Type command changes the ID number which describes what type a +partition is. `fdisk' currently recognises 30 system IDs, in the sense +that it prints a string for each of them, but it allows you to change +any system ID to any other, with the following exceptions: you may not +change any partition to or from the type Extended, and you may not +change a partition whose type is Empty (0) to any other type. You may, +however, change the type of any data partition to 0, which is +equivalent to deleting it. + +The new system ID or type code is a hexadecimal number. There are +two ways of listing the numbers which `fdisk' recognises: use the List +command, which prints the list, or use the Type command, which, when it +prompts you for the code, says + + Hex code (type L to list codes): _ + +where the upper case `L' is used for clarity. The codes printed are: +Some of these numbers are a trifle uncertain. By default `fdisk' uses +a type of 83. It used to use 81, the type code used by the MINIX +`fdisk'. It seemed prudent to change the default since (a) many Linux +`minix' file systems are no longer compatible with MINIX, (b) the ext2 +file system, a native Linux file system, is fairly stable, as is the +Xia file system, and (c) the number 81 causes problems with DR-DOS. +Linux does not usually care what values you use for type codes, but +other systems, in particular DOS, OS/2, and DR-DOS, may. + +The value of 82 for Linux swap partitions is my own invention, and +is intended to give some recognisable distinction to the partitions +when the values are displayed in hexadecimal. + +New active flags and new system type codes are not written to the +disk until you exit from `fdisk' with the Write command, as described +above, in the section on deleting and adding partitions. + + +Extra commands for experts +-------------------------- + +The eXtra command `x' puts `fdisk' into `expert' mode, in which a +slightly different set of commands is available. The Active, Delete, +List, New, Type, Verify, and `eXpert' commands are not available in +expert mode. The commands Write and Quit are available as in ordinary +mode, the Print command is available, but produces output in a slightly +different format, and of course the Menu command prints the expert +menu. There are several new commands. + + 1. The Return command brings you back to the main menu. + + 2. The Extended command prints the list of table entries which point + to other tables. Ordinary users do not need this information. + The data is shown as it is stored. The same format is used for + the expert Print command. + + 3. The dangerous Begin command allows you to move the start of data + in a partition away from its beginning. Other systems create + partitions with this format, and it is sometimes useful to be able + to reproduce it. + + 4. The slightly dangerous Cylinders command allows you to change the + available number of cylinders. For SCSI disk owners, note that we + require not the actual number of physical cylinders, but the + number of logical cylinders used by DOS and other operating + systems. + + 5. The extremely dangerous Heads and Sectors commands allow you to + change the number of heads and sectors. It should not be + necessary to use these commands unless you have a SCSI disk, whose + geometry Linux is not always able to determine. SCSI disk owners + note that we need not the actual number of heads or of sectors per + track, but the number believed to exist by DOS and other operating + systems. *Warning*: If you set either of these numbers to a bad + value, you may lose all data on your disk. + +Always, after giving any of the commands Begin, Cylinder, Heads, or +Sectors, you should Return to the main menu and give the Verify command. + + +Warnings for `fdisk' users +-------------------------- + +In general, you should not use this `fdisk' program to create +partitions for other operating systems, only for Linux. Nor should you +use `fdisk' commands from other operating systems to create partitions +for Linux. + +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. + +Partitioning a hard disk may destroy data which is on that disk if you +are not careful. Go slowly, write down a description of the partition +tables before you changed them, and always verify before you write. + +Most operating systems and utilities expect that all partitions begin and +end at cylinder boundaries. This version of `fdisk' does so by default, +but you can use it to create partitions which begin or end anywhere. +This does not normally affect Linux, but it is very dangerous, as other +operating systems (including DOS) may try to `correct' the partition +boundaries. + +It is dangerous to create a new partition when the display/entry +units are sectors. + +The Verify command warns you if anything is wrong. *Always* give a +Verify command before writing any changes to disk. + +If you set the disk geometry (tracks per cylinder, or sectors per +track) to an incorrect value, you may lose all data on your disk. + +Do create BSD/SUN and/or IRIX/SGI disk labels only when you are sure +that you want them. Both features are intended to allow you READing +those labels and prevent unintentional formatting of these disks. diff --git a/fdisk/cfdisk.8 b/fdisk/cfdisk.8 new file mode 100644 index 000000000..c8391af2a --- /dev/null +++ b/fdisk/cfdisk.8 @@ -0,0 +1,406 @@ +.\" 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 "3 June 1995" "The BOGUS Linux Release" "Linux Programmer's Manual" +.SH NAME +cfdisk \- Curses based disk partition table manipulator for Linux +.SH SYNOPSIS +.BI "cfdisk [ \-avz ] [ \-c " cylinders " ] [ \-h " heads " ]" +.BI "[ \-s " sectors-per-track " ] [ -P " opt " ] [ " device " ]" +.SH DESCRIPTION +.B cfdisk +is a curses based program for partitioning a hard disk drive. The +.I device +can be any one of the following: +.sp +.nf +.RS +/dev/hda [default] +/dev/hdb +/dev/sda +/dev/sdb +/dev/sdc +/dev/sdd +.RE +.fi + +.B cfdisk +first tries to read the geometry of the hard disk. If it fails, an +error message is displayed and +.B cfdisk +exits. This should only happen when partitioning a SCSI drive on an +adapter without a BIOS. To correct this problem, you can set the +.IR cylinders ", " heads " and " sectors-per-track +on the command line. 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 asterisks +.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 +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 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 display 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 partition's 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 asterisks +.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 works is most cases, but I have seen it fail. Don't +panic. It will be correct after you reboot the system. In all cases, +I still recommend rebooting the system--just to be safe. +.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 \-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" +fdisk(8) +.SH BUGS +The current version does not support multiple disks. +.SH AUTHOR +Kevin E. Martin (martin@cs.unc.edu) diff --git a/fdisk/cfdisk.c b/fdisk/cfdisk.c new file mode 100644 index 000000000..96ddbbf24 --- /dev/null +++ b/fdisk/cfdisk.c @@ -0,0 +1,2642 @@ +/**************************************************************************** + * + * 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 cfdisk; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, 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-n: aeb@cwi.nl + * 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 + * 2: hardware problems [BAD_SEEK, BAD_READ, BAD_WRITE or BAD_OPEN]. + * 3: ioctl(fd, HDIO_GETGEO,...) failed [BAD_GEOMETRY]. + * 4: bad partition table on disk. [BAD_PRIMARY or BAD_LOGICAL]. + * + ****************************************************************************/ + +#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 SLCURSES + #include <slcurses.h> +#else +#if NCH + #include <ncurses.h> +#else + #include <curses.h> +#endif +#endif +#include <signal.h> +#include <math.h> +#include <locale.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <linux/types.h> +#include <linux/hdreg.h> +#include <linux/fs.h> /* for BLKRRPART */ + +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +extern ext2_loff_t ext2_llseek(unsigned int fd, ext2_loff_t offset, + unsigned int origin); + +#define VERSION "0.8n" + +#define DEFAULT_DEVICE "/dev/hda" +#define ALTERNATE_DEVICE "/dev/sda" + +#define LINE_LENGTH 80 +#define MAXIMUM_PARTS 60 + +#define SECTOR_SIZE 512 + +#define MAX_CYLINDERS 65535 +#define MAX_HEADS 255 +#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 ADD_EXISTS "This partition is already in use" +#define ADD_UNUSABLE "This partition is unusable" +#define DEL_EMPTY "Cannot delete an empty partition" +#define ID_EMPTY "Cannot change FS Type to empty" +#define ID_EXT "Cannot change FS Type to extended" +#define NEED_EXT "No room to create the extended partition" +#define NO_FLAGS "Cannot make this partition bootable" +#define NO_MORE_PARTS "No more partitions" +#define PRINT_OPEN_ERR "Cannot open file '%s'" +#define TWO_EXTENDEDS "Cannot create logical drive here -- would create two extended partitions" +#define TYPE_EMPTY "Cannot change the type of an empty partition" +#define BAD_COMMAND "Illegal command" +#define MAX_UNMAXABLE "Cannot maximize this partition" +#define BAD_OPEN "Cannot open disk drive" +#define BAD_SEEK "Cannot seek on disk drive" +#define BAD_READ "Cannot read disk drive" +#define BAD_WRITE "Cannot write disk drive" +#define BAD_GEOMETRY "Cannot read disk drive geometry" +#define BAD_PRIMARY "Bad primary partition" +#define BAD_LOGICAL "Bad logical partition" +#define BAD_CYLINDERS "Illegal cylinders value" +#define BAD_HEADS "Illegal heads value" +#define BAD_SECTORS "Illegal sectors value" +#define READONLY_WARN "Opened disk read-only - you have no permission to write" +#define WRITE_WARN "Warning!! This may destroy data on your disk!" +#define YES_NO "Please enter `yes' or `no'" +#define WRITING_PART "Writing partition table to disk..." +#define YES_WRITE "Wrote partition table to disk" +#define NO_WRITE "Did not write partition table to disk" +#define RRPART_FAILED "Wrote partition table, but re-read table failed. Reboot to update table." +#define NOT_DOS_MBR_BOOTABLE "Not precisely one primary partition is bootable. DOS MBR cannot boot this." + +#define PRI_OR_LOG -1 +#define PRIMARY -2 +#define LOGICAL -3 + +#define COL_ID_WIDTH 20 + +#define CR '\015' +#define ESC '\033' +#define DEL '\177' +#define BELL '\007' +/* '\014' == ^L */ +#define REDRAWKEY '\014' + +/* Display units */ +#define MEGABYTES 1 +#define SECTORS 2 +#define CYLINDERS 3 + +#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))) + +#define set_hsc(h,s,c,sector) \ +{ \ + s = sector % sectors + 1; \ + sector /= sectors; \ + h = sector % heads; \ + sector /= heads; \ + c = sector & 0xFF; \ + s |= (sector >> 2) & 0xC0;\ +} + +#define is_extended(x) ((x) == DOS_EXTENDED || (x) == WIN98_EXTENDED || \ + (x) == LINUX_EXTENDED) + +#define is_dos_partition(x) ((x) == 1 || (x) == 4 || (x) == 6) +#define may_have_dos_label(x) (is_dos_partition(x) \ + || (x) == 7 || (x) == 0xb || (x) == 0xc || (x) == 0xe \ + || (x) == 0x11 || (x) == 0x14 || (x) == 0x16 || (x) == 0x17) + +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 */ +}; + +/* start_sect and nr_sects are stored little endian on all machines */ +/* moreover, they are not aligned correctly */ +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); +} + +unsigned int +read4_little_endian(unsigned char *cp) { + return (uint)(cp[0]) + ((uint)(cp[1]) << 8) + + ((uint)(cp[2]) << 16) + ((uint)(cp[3]) << 24); +} + +void +set_start_sect(struct partition *p, unsigned int start_sect) { + store4_little_endian(p->start4, start_sect); +} + +unsigned int +get_start_sect(struct partition *p) { + return read4_little_endian(p->start4); +} + +void +set_nr_sects(struct partition *p, unsigned int nr_sects) { + store4_little_endian(p->size4, nr_sects); +} + +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 { + int first_sector; /* first sector in partition */ + int last_sector; /* last sector in partition */ + int 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 8 + char fstype[FSTYPESZ+1]; +} partition_info; + +char *disk_device = DEFAULT_DEVICE; +int fd; +int heads = 0; +int sectors = 0; +int cylinders = 0; +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; +int logical_sectors[MAXIMUM_PARTS]; + +__sighandler_t old_SIGINT, old_SIGTERM; + +int arrow_cursor = FALSE; +int display_units = MEGABYTES; +int zero_table = 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 = 5; +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 = 70; +int COMMAND_LINE_X = 5; + +#define NUM_PART_TYPES 256 +char *partition_type[NUM_PART_TYPES] = { + [LINUX_MINIX] = "Linux/MINIX", + [LINUX_SWAP] = "Linux Swap", + [LINUX] = "Linux", + [FREE_SPACE] = "Free Space", + [DOS_EXTENDED]= "Extended", + [LINUX_EXTENDED] = "Linux extended", + [0x01] = "DOS FAT12", + [0x02] = "XENIX root", + [0x03] = "XENIX usr", + [0x04] = "DOS FAT16", + [0x06] = "DOS FAT16 (big)", + [OS2_OR_NTFS] = "OS/2 HPFS or NTFS", + [0x08] = "AIX", + [0x09] = "AIX bootable", + [0x0A] = "OS/2 Boot Manager", + [0x0B] = "Win95 FAT32", + [0x0C] = "Win95 FAT32 (LBA)", + [0x0E] = "Win95 FAT16 (LBA)", + [0x0F] = "Win95 Extended (LBA)", + [0x11] = "Hidden DOS FAT12", + [0x14] = "Hidden DOS FAT16", + [0x16] = "Hidden DOS FAT16 (big)", + [0x17] = "Hidden OS/2 HPFS or NTFS", + [0x40] = "Venix 80286", + [0x41] = "PPC PReP boot", + [0x51] = "Novell?", + [0x52] = "Microport", + [0x63] = "GNU HURD", + [0x64] = "Novell Netware 286", + [0x65] = "Novell Netware 386", + [0x75] = "PC/IX", + [0x80] = "Old MINIX", + [0x93] = "Amoeba", + [0x94] = "Amoeba BBT", + [0xA5] = "BSD/386", + [0xA6] = "OpenBSD", + [0xA7] = "NEXTSTEP", + [0xB7] = "BSDI fs", + [0xB8] = "BSDI swap", + [0xC7] = "Syrinx", + [0xDB] = "CP/M", + [0xE1] = "DOS access", + [0xE3] = "DOS R/O", + [0xEB] = "BeOS fs", + [0xF2] = "DOS secondary", + [0xFF] = "BBT" +}; + +/* Some libc's have their own basename() */ +char *my_basename(char *devname) +{ + char *s = rindex(devname, '/'); + return s ? s+1 : devname; +} + +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].id == LINUX) { + if (!strcmp(p_info[i].fstype, "ext2")) + return "Linux ext2"; + else + return "Linux"; + } else if (p_info[i].id == OS2_OR_NTFS) { + if (!strncmp(p_info[i].fstype, "HPFS", 4)) + return "OS/2 HPFS"; + else if (!strncmp(p_info[i].ostype, "OS2", 3)) + return "OS/2 IFS"; + else if (!p_info[i].ostype) + return p_info[i].ostype; + else + return "NTFS"; + } else + return partition_type[p_info[i].id]; +} + +void fdexit(int ret) +{ + if (opened) + close(fd); + + if (changed) { + fprintf(stderr, "Disk has been changed.\n"); + fprintf(stderr, "Reboot the system to ensure the partition " + "table is correctly updated.\n"); + + 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); +} + +int get_string(char *str, int len, char *def) +{ + char c; + int i = 0; + int x, y; + int use_def = FALSE; + + getyx(stdscr, y, x); + clrtoeol(); + + str[i] = 0; + + if (def != NULL) { + mvaddstr(y, x, def); + move(y, x); + use_def = TRUE; + } + + refresh(); + while ((c = getch()) != '\n' && c != CR) { + switch (c) { + case ESC: + move(y, x); + clrtoeol(); + refresh(); + return GS_ESCAPE; + case DEL: + case '\b': + if (i > 0) { + str[--i] = 0; + mvaddch(y, x+i, ' '); + move(y, x+i); + } else if (use_def) { + clrtoeol(); + use_def = FALSE; + } else + putchar(BELL); + break; + default: + if (i < len && isprint(c)) { + mvaddch(y, x+i, c); + if (use_def) { + clrtoeol(); + use_def = FALSE; + } + str[i++] = c; + str[i] = 0; + } else + putchar(BELL); + } + refresh(); + } + + if (use_def) + return GS_DEFAULT; + else + return i; +} + +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; +} + +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; + } +} + +void die_x(int ret); + +void fatal(char *s, int ret) +{ + char str[LINE_LENGTH]; + + if (curses_started) { + sprintf(str, "FATAL ERROR: %s", s); + mvaddstr(WARNING_START, (COLS-strlen(str))/2, str); + sprintf(str, "Press any key to exit fdisk"); + mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str); + putchar(BELL); /* CTRL-G */ + refresh(); + (void)getch(); + die_x(ret); + } else { + fprintf(stderr, "FATAL ERROR: %s\n", s); + exit(ret); + } +} + +void die(int dummy) +{ + die_x(0); +} + +void die_x(int ret) +{ + signal(SIGINT, old_SIGINT); + signal(SIGTERM, old_SIGTERM); +#ifdef SLCURSES + SLsmg_gotorc(LINES-1, 0); + SLsmg_refresh(); +#else + mvcur(0, COLS-1, LINES-1, 0); +#endif + nl(); + endwin(); + printf("\n"); + fdexit(ret); +} + +void read_sector(char *buffer, int sect_num) +{ + if (ext2_llseek(fd, ((ext2_loff_t) sect_num)*SECTOR_SIZE, SEEK_SET) < 0) + fatal(BAD_SEEK, 2); + if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE) + fatal(BAD_READ, 2); +} + +void write_sector(char *buffer, int sect_num) +{ + if (ext2_llseek(fd, ((ext2_loff_t) sect_num)*SECTOR_SIZE, SEEK_SET) < 0) + fatal(BAD_SEEK, 2); + if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE) + fatal(BAD_WRITE, 2); +} + +void dos_copy_to_info(char *to, int tosz, char *from, int fromsz) { + int i; + + for(i=0; i<tosz && i<fromsz && isascii(from[i]); i++) + to[i] = from[i]; + to[i] = 0; +} + +void get_dos_label(int i) +{ + char sector[128]; +#define DOS_OSTYPE_OFFSET 3 +#define DOS_LABEL_OFFSET 43 +#define DOS_FSTYPE_OFFSET 54 +#define DOS_OSTYPE_SZ 8 +#define DOS_LABEL_SZ 11 +#define DOS_FSTYPE_SZ 8 + ext2_loff_t offset; + + offset = ((ext2_loff_t) p_info[i].first_sector + p_info[i].offset) + * SECTOR_SIZE; + if (ext2_llseek(fd, offset, SEEK_SET) == offset + && read(fd, §or, sizeof(sector)) == sizeof(sector)) { + dos_copy_to_info(p_info[i].ostype, OSTYPESZ, + sector+DOS_OSTYPE_OFFSET, DOS_OSTYPE_SZ); + dos_copy_to_info(p_info[i].volume_label, LABELSZ, + sector+DOS_LABEL_OFFSET, DOS_LABEL_SZ); + dos_copy_to_info(p_info[i].fstype, FSTYPESZ, + sector+DOS_FSTYPE_OFFSET, DOS_FSTYPE_SZ); + } +} + +void get_ext2_label(int i) +{ +#define EXT2_SUPER_MAGIC 0xEF53 +#define EXT2LABELSZ 16 + struct ext2_super_block { + char s_dummy0[56]; + unsigned char s_magic[2]; + char s_dummy1[62]; + char s_volume_name[EXT2LABELSZ]; + char s_last_mounted[64]; + char s_dummy2[824]; + } sb; + char *label = sb.s_volume_name; + ext2_loff_t offset; + int j; + + offset = ((ext2_loff_t) p_info[i].first_sector + p_info[i].offset) + * SECTOR_SIZE + 1024; + if (ext2_llseek(fd, offset, SEEK_SET) == offset + && read(fd, &sb, sizeof(sb)) == sizeof(sb) + && sb.s_magic[0] + 256*sb.s_magic[1] == EXT2_SUPER_MAGIC) { + for(j=0; j<EXT2LABELSZ; j++) + if(!isprint(label[j])) + label[j] = 0; + label[EXT2LABELSZ] = 0; + strncpy(p_info[i].volume_label, label, LABELSZ); + strncpy(p_info[i].fstype, "ext2", FSTYPESZ); + } +} + +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 */ + } + } +} + +void remove_part(int i) +{ + int p; + + for (p = i; p < num_parts; p++) + p_info[p] = p_info[p+1]; + + num_parts--; +} + +void insert_empty_part(int i, int first, int 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++; +} + +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 = sectors*heads*cylinders - 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(); +} + +int add_part(int num, int id, int flags, int first, int last, int offset, + int want_label) +{ + int i, pri = 0, log = 0; + + if (num_parts == MAXIMUM_PARTS || + first < 0 || + first >= cylinders*heads*sectors || + last < 0 || + last >= cylinders*heads*sectors) { + return -1; /* bad start or end */ + } + + 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 + || 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(TWO_EXTENDEDS); + 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(TWO_EXTENDEDS); + 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; + if (want_label) { + if (may_have_dos_label(id)) + get_dos_label(i); + else if (id == LINUX) + get_ext2_label(i); + } + + check_part_info(); + + return 0; +} + +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; +} + +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; +} + +void inc_logical(int i) +{ + int j; + + for (j = i; j < num_parts; j++) + if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num)) + p_info[j].num++; +} + +/* Command menu support by Janne Kukonlehto <jtklehto@phoenix.oulu.fi> September 1994 */ + +/* Constants for menuType parameter of menuSelect function */ +#define MENU_HORIZ 1 +#define MENU_VERT 2 +#define MENU_ACCEPT_OTHERS 4 +#define MENU_BUTTON 8 +/* Miscellenous constants */ +#define MENU_SPACING 2 +#define MENU_MAX_ITEMS 256 /* for simpleMenu function */ +#define MENU_UP 1 +#define MENU_DOWN 2 +#define MENU_RIGHT 3 +#define MENU_LEFT 4 + +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. + */ + +int menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength, + char *available, int menuType, int current ) +{ + int i, lmargin = x, ymargin = y; + /* Print available buttons */ + move( y, x ); clrtoeol(); + for( i = 0; menuItems[i].key; i++ ) + { + char buff[20]; + int lenName; + /* 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 */ + lenName = strlen( menuItems[i].name ); + if(lenName > itemLength) + print_warning("Menu item too long. Menu may look odd."); + if( menuType & MENU_BUTTON ) + sprintf( buff, "[%*s%-*s]", (itemLength - lenName) / 2, "", + (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name ); + else + sprintf( buff, "%*s%-*s", (itemLength - lenName) / 2, "", + (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name ); + mvaddstr( y, x, buff ); + /* Lowlight after selected item */ + if( current == i ) /*attroff( A_REVERSE )*/ standend (); + /* Calculate position for the next item */ + if( menuType & MENU_VERT ) + { + y += 1; + if( y >= WARNING_START ) + { + y = ymargin; + x += itemLength + MENU_SPACING; + if( menuType & MENU_BUTTON ) x += 2; + } + } + else + { + x += itemLength + MENU_SPACING; + if( menuType & MENU_BUTTON ) x += 2; + if( x > COLUMNS - lmargin - 12 ) + { + x = lmargin; + y ++ ; + } + } + } + /* Print the description of selected item */ + mvaddstr( WARNING_START + 1, + (COLUMNS - strlen( menuItems[current].desc )) / 2, + menuItems[current].desc ); + return y; +} + +/* This function takes a list of menu items, lets the user choose one of them * + * and returns the value keyboard shortcut of the selected menu item */ + +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; + if( !( menuType & ( MENU_HORIZ | MENU_VERT ) ) ) + { + print_warning("Menu without direction. Defaulting horizontal."); + menuType |= MENU_HORIZ; + } + /* Make sure that the current is one of the available items */ + while( !strchr(available, menuItems[current].key) ) + { + current ++ ; + if( !menuItems[current].key ) current = 0; + } + /* Repeat until allowable choice has been made */ + while( !key ) + { + /* Display the menu */ + ylast = menuUpdate( y, x, menuItems, itemLength, available, + menuType, current ); + refresh(); + key = getch(); + /* Clear out all prompts and such */ + clear_warning(); + for( i = y; i < ylast; i ++ ) + { + move( i, x ); + clrtoeol(); + } + move( WARNING_START + 1, 0 ); + clrtoeol(); + /* Cursor keys */ + if( key == ESC ) + { + /* Check whether this is a real ESC or one of extended keys */ + /*nodelay(stdscr, TRUE);*/ + key = getch(); + /*nodelay(stdscr, FALSE);*/ + if( key == /*ERR*/ ESC ) + { + /* This is a real ESC */ + key = ESC; + } + if( key == '[' ) + { + /* This is one extended keys */ + switch( getch() ) + { + case 'A': /* Up arrow */ + if( menuType & MENU_VERT ) + { + do { + current -- ; + if( current < 0 ) while( menuItems[current+1].key ) current ++ ; + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_UP; + break; + case 'B': /* Down arrow */ + if( menuType & MENU_VERT ) + { + do { + current ++ ; + if( !menuItems[current].key ) current = 0 ; + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_DOWN; + break; + case 'C': /* Right arrow */ + if( menuType & MENU_HORIZ ) + { + do { + current ++ ; + if( !menuItems[current].key ) + { + current = 0 ; + } + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_RIGHT; + break; + case 'D': /* Left arrow */ + if( menuType & MENU_HORIZ ) + { + do { + current -- ; + if( current < 0 ) + { + while( menuItems[current + 1].key ) current ++ ; + } + } while( !strchr( available, menuItems[current].key ) ); + key = 0; + } + else + key = MENU_LEFT; + break; + } + } + } + /* Enter equals to the keyboard shortcut of current menu item */ + if( key == 13 ) + { + key = menuItems[current].key; + } + /* 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"); + } + } + /* 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? */ + +void menuContinue(void) +{ + static struct MenuItem menuContinueBtn[]= + { + { 'c', "", "Press a key to continue" }, + { 0, NULL, NULL } + }; + + menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, + menuContinueBtn, 0, "c", MENU_HORIZ | MENU_ACCEPT_OTHERS, 0 ); +} + +/* Function menuSelect takes way too many parameters * + * Luckily, most of time we can do with this function */ + +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_HORIZ | MENU_BUTTON, menuDefault); +} + +/* End of command menu support code */ + +void new_part(int i) +{ + char response[LINE_LENGTH], def[LINE_LENGTH]; + char c; + int first = p_info[i].first_sector; + int last = p_info[i].last_sector; + int offset = 0; + int flags = 0; + int id = LINUX; + int num = -1; + int num_sects = last - first + 1; + int len, ext, j; + + if (p_info[i].num == PRI_OR_LOG) { + static struct MenuItem menuPartType[]= + { + { 'p', "Primary", "Create a new primary partition" }, + { 'l', "Logical", "Create a new logical partition" }, + { ESC, "Cancel", "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 !!!"); + + sprintf(def, "%.2f", ceiling(num_sects/20.48)/100); + 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/(sectors*heads))) + for (j = 0; + j < len-1 && (isdigit(response[j]) || response[j] == '.'); + j++); + if (toupper(response[j]) == 'K') { + num_sects = num_cyls(atof(response)*1024)*sectors*heads; + } else if (toupper(response[j]) == 'M') { + num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads; + } else if (toupper(response[j]) == 'C') { + num_sects = round_int(atof(response))*sectors*heads; + } else if (toupper(response[j]) == 'S') { + num_sects = round_int(atof(response)); + } else { + num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads; + } + } + + 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', "Beginning", "Add partition at beginning of free space" }, + { 'e', "End", "Add partition at end of free space" }, + { ESC, "Cancel", "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(NEED_EXT); + return; + } + (void) add_part(ext, DOS_EXTENDED, 0, first, last, + (first == 0 ? sectors : 0), 0); + first = ext_info.first_sector + ext_info.offset; + } + + if (IS_LOGICAL(num)) + inc_logical(i); + + /* Now we have a complete partition to ourselves */ + if (first == 0 || IS_LOGICAL(num)) + offset = sectors; + + (void) add_part(num, id, flags, first, last, offset, 0); +} + +void clear_p_info(void) +{ + num_parts = 1; + p_info[0].first_sector = 0; + p_info[0].last_sector = sectors*heads*cylinders - 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; +} + +void fill_p_info(void) +{ + int pn, i, bs, bsz; + struct partition *p; + struct hd_geometry geometry; + partition_table buffer; + partition_info tmp_ext = { 0, 0, 0, 0, FREE_SPACE, PRIMARY }; + + if ((fd = open(disk_device, O_RDWR)) < 0) { + if ((fd = open(disk_device, O_RDONLY)) < 0) + fatal(BAD_OPEN, 2); + opentype = O_RDONLY; + print_warning(READONLY_WARN); + if (curses_started) { + refresh(); + getch(); + clear_warning(); + } + } else + opentype = O_RDWR; + opened = TRUE; + + /* 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 */ + + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { + if (!heads) + heads = geometry.heads; + if (!sectors) + sectors = geometry.sectors; + if (!cylinders) + cylinders = geometry.cylinders; + } + + if (!heads || !sectors || !cylinders) + fatal(BAD_GEOMETRY, 3); /* probably a file or cdrom */ + + read_sector(buffer.c.b, 0); + + clear_p_info(); + + if (!zero_table) { + 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)) { + fatal(BAD_PRIMARY, 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)) + fatal(BAD_LOGICAL, 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); + } + } +} + +void fill_part_table(struct partition *p, partition_info *pi) +{ + int sects; + + p->boot_ind = pi->flags; + p->sys_ind = pi->id; + if (IS_LOGICAL(pi->num)) + set_start_sect(p,pi->offset); + else + set_start_sect(p,pi->first_sector + pi->offset); + set_nr_sects(p, pi->last_sector - (pi->first_sector+pi->offset) + 1); + sects = (((pi->first_sector+pi->offset)/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->first_sector+pi->offset); + set_hsc(p->head, p->sector, p->cyl, sects); + sects = ((pi->last_sector/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->last_sector); + set_hsc(p->end_head, p->end_sector, p->end_cyl, sects); +} + +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; +} + +void fill_logical_table(partition_table *buffer, partition_info *pi) +{ + struct partition *p; + int i, sects; + + 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); + sects = ((pi->first_sector/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->first_sector); + set_hsc(p->head, p->sector, p->cyl, sects); + sects = ((pi->last_sector/(sectors*heads) > 1023) ? + heads*sectors*1024 - 1 : pi->last_sector); + set_hsc(p->end_head, p->end_sector, p->end_cyl, sects); + } + + buffer->p.magicflag[0] = PART_TABLE_FLAG0; + buffer->p.magicflag[1] = PART_TABLE_FLAG1; +} + +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(READONLY_WARN); + 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(WRITE_WARN); + + while (!done) { + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Are you sure you want 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 (len == 2 && + toupper(response[0]) == 'N' && + toupper(response[1]) == 'O') { + print_warning(NO_WRITE); + return; + } else if (len == 3 && + toupper(response[0]) == 'Y' && + toupper(response[1]) == 'E' && + toupper(response[2]) == 'S') + done = TRUE; + else + print_warning(YES_NO); + } + + clear_warning(); + print_warning(WRITING_PART); + 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) { + sync(); + sleep(2); + if (!ioctl(fd,BLKRRPART)) + changed = TRUE; + sync(); + sleep(4); + + clear_warning(); + if (changed) + print_warning(YES_WRITE); + else + print_warning(RRPART_FAILED); + } else + print_warning(YES_WRITE); + + /* 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 != 1) + print_warning(NOT_DOS_MBR_BOOTABLE); +} + +void fp_printf(FILE *fp, char *format, ...) +{ + va_list args; + char buf[1024]; + int y, x; + + va_start(args, format); + vsprintf(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 +void print_file_buffer(FILE *fp, 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", (unsigned char) 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"); +} + +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]; + sprintf(errstr, PRINT_OPEN_ERR, 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(); + } +} + +void print_p_info_entry(FILE *fp, partition_info *p) +{ + int 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, "%8d%c", p->first_sector, + ((p->first_sector/(sectors*heads)) != + ((float)p->first_sector/(sectors*heads)) ? + '*' : ' ')); + + fp_printf(fp, "%8d%c", p->last_sector, + (((p->last_sector+1)/(sectors*heads)) != + ((float)(p->last_sector+1)/(sectors*heads)) ? + '*' : ' ')); + + fp_printf(fp, "%7d%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, "%8d%c", size, + ((size/(sectors*heads)) != ((float)size/(sectors*heads)) ? + '*' : ' ')); + + fp_printf(fp, " "); + + if (p->id == UNUSABLE) + sprintf(part_str, "%.17s", "Unusable"); + else if (p->id == FREE_SPACE) + sprintf(part_str, "%.17s", "Free Space"); + else if (partition_type[p->id]) + sprintf(part_str, "%.17s (%02X)", partition_type[p->id], p->id); + else + sprintf(part_str, "%.17s (%02X)", "Unknown", p->id); + fp_printf(fp, "%-22.22s", part_str); + + fp_printf(fp, " "); + + if (p->flags == ACTIVE_FLAG) + fp_printf(fp, "Boot (%02X)", p->flags); + else if (p->flags != 0) + fp_printf(fp, "Unknown (%02X)", p->flags); + else + fp_printf(fp, "None (%02X)", p->flags); + + fp_printf(fp, "\n"); +} + +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]; + sprintf(errstr, PRINT_OPEN_ERR, 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) Flags\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(); + } +} + +void print_part_entry(FILE *fp, int num, partition_info *pi) +{ + int first = 0, start = 0, end = 0, size = 0; + int ss = 0, sh = 0, sc = 0; + int es = 0, eh = 0, ec = 0; + int flags = 0, id = 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; + if ((start/(sectors*heads)) > 1023) + start = heads*sectors*1024 - 1; + if ((end/(sectors*heads)) > 1023) + end = heads*sectors*1024 - 1; + + ss = start % sectors + 1; + start /= sectors; + sh = start % heads; + sc = start / heads; + + es = end % sectors + 1; + end /= sectors; + eh = end % heads; + ec = end / heads; + } + + fp_printf(fp, "%2d 0x%02X %4d %4d %4d 0x%02X %4d %4d %4d %8d %9d\n", + num+1, flags, sh, ss, sc, id, eh, es, ec, first, size); +} + + +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]; + sprintf(errstr, PRINT_OPEN_ERR, 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, " ---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(); + } +} + +void print_tables(void) +{ + int done = FALSE; + + static struct MenuItem menuFormat[]= + { + { 'r', "Raw", "Print the table using raw data format" }, + { 's', "Sectors", "Print the table ordered by sectors" }, + { 't', "Table", "Just print the partition table" }, + { ESC, "Cancel", "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!" +#define NEW_HELP_SCREEN "SNHS!" +void display_help() +{ + char *help_text[] = { + "Help Screen for cfdisk " VERSION, + "", + "This is cfdisk, a curses based disk partitioning programs, which", + "allows you to create, delete and modify partitions on your hard", + "disk drive.", + "", + "Copyright (C) 1994-1998 Kevin E. Martin & aeb", + "", + "Command Meaning", + "------- -------", + " b Toggle bootable flag of the current partition", + " d Delete the current partition", + " g Change cylinders, heads, sectors-per-track parameters", + " WARNING: This option should only be used by people who", + " know what they are doing.", + " h Print this screen", + " m Maximize disk usage of the current partition", + " Note: This may make the partition incompatible with", + " DOS, OS/2, ...", + " n Create new partition from free space", + " p Print partition table to the screen or to a file", + " There are several different formats for the partition", + " that you can choose from:", + " r - Raw data (exactly what would be written to disk)", + " s - Table ordered by sectors", + " t - Table in raw format", + " q Quit program without writing partition table", + " t Change the filesystem type", + " u Change units of the partition size display", + " Rotates through Mb, sectors and cylinders", + " W Write partition table to disk (must enter upper case W)", + " Since this might destroy data on the disk, you must", + " either confirm or deny the write by entering `yes' or", + " `no'", + "Up Arrow Move cursor to the previous partition", + "Down Arrow Move cursor to the next partition", + "CTRL-L Redraws the screen", + " ? Print this screen", + "", + "Note: All of the commands can be entered with either upper or lower", + "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 (!strcmp(help_text[cur_line], NEW_HELP_SCREEN)) { + menuContinue(); + erase(); + move(0, 0); + cur_line++; + } else + fp_printf(fp, "%s\n", help_text[cur_line++]); + + menuContinue(); +} + +int change_geometry(void) +{ + int ret_val = FALSE; + int done = FALSE; + char def[LINE_LENGTH]; + char response[LINE_LENGTH]; + int tmp_val; + + while (!done) { + static struct MenuItem menuGeometry[]= + { + { 'c', "Cylinders", "Change cylinder geometry" }, + { 'h', "Heads", "Change head geometry" }, + { 's', "Sectors", "Change sector geometry" }, + { 'd', "Done", "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, "%d", cylinders); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, + "Enter the number of cylinders: "); + if (get_string(response, LINE_LENGTH, def) > 0) { + tmp_val = atoi(response); + if (tmp_val > 0 && tmp_val <= MAX_CYLINDERS) { + cylinders = tmp_val; + ret_val = TRUE; + } else + print_warning(BAD_CYLINDERS); + } + 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 = atoi(response); + if (tmp_val > 0 && tmp_val <= MAX_HEADS) { + heads = tmp_val; + ret_val = TRUE; + } else + print_warning(BAD_HEADS); + } + 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 = atoi(response); + if (tmp_val > 0 && tmp_val <= MAX_SECTORS) { + sectors = tmp_val; + ret_val = TRUE; + } else + print_warning(BAD_SECTORS); + } + break; + case ESC: + case 'D': + done = TRUE; + break; + default: + putchar(BELL); + break; + } + } + + if (ret_val) { + int disk_end = heads*sectors*cylinders-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; +} + +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; + int j, pos; + + for (num_types = 0, j = 1; j < NUM_PART_TYPES; j++) + if (partition_type[j]) + num_types++; + + 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 > DISK_TABLE_START+cur_part+4) + y_start = DISK_TABLE_START+cur_part+4; + y_end = y_start + num_down - 1; + + for (j = y_start - 1; j <= y_end + 1; j++) { + move(j, 0); + clrtoeol(); + } + + for (pos = 0, j = 1; j < NUM_PART_TYPES; j++) + if (partition_type[j]) { + move(y_start + pos % num_down, (pos/num_down)*COL_ID_WIDTH + 1); + printw("%02X %-16.16s", j, partition_type[j]); + pos++; + } + + sprintf(def, "%02X", new_id); + mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Enter filesystem type: "); + if ((len = get_string(id, 2, 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(ID_EMPTY); + else if (is_extended(new_id)) + print_warning(ID_EXT); + else + p_info[i].id = new_id; +} + +void draw_partition(int i) +{ + int size, j; + int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN; + char *t; + + if (!arrow_cursor) { + move(y, 0); + for (j = 0; j < COLS; j++) + addch(' '); + } + + if (p_info[i].id > 0) { + mvprintw(y, NAME_START, + "%s%d", my_basename(disk_device), 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; + if (display_units == SECTORS) + mvprintw(y, SIZE_START, "%9d", size); + else if (display_units == CYLINDERS) + mvprintw(y, SIZE_START, "%9d", size/(sectors*heads)); + else + mvprintw(y, SIZE_START, "%9.2f", ceiling(size/20.48)/100); + if (((size/(sectors*heads)) != ceiling(size/(sectors*(float)heads))) || + ((p_info[i].first_sector/(sectors*heads)) != + ceiling(p_info[i].first_sector/(sectors*heads)))) + mvprintw(y, COLUMNS-1, "*"); +} + +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; + } +} + +void draw_screen(void) +{ + int i; + char *line; + + line = (char *)malloc((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); + + + sprintf(line, "cfdisk %s", VERSION); + mvaddstr(HEADER_START, (COLS-strlen(line))/2, line); + sprintf(line, "Disk Drive: %s", disk_device); + mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line); + sprintf(line, "Heads: %d Sectors per Track: %d Cylinders: %d", + heads, sectors, cylinders); + mvaddstr(HEADER_START+3, (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 + mvaddstr(DISK_TABLE_START, SIZE_START, "Size (MB)"); + + 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); +} + +int draw_cursor(int move) +{ + if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts)) + return -1; + + 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(); + } + + return 0; +} + +void do_curses_fdisk(void) +{ + int done = FALSE; + char command; + + static struct MenuItem menuMain[]= + { + { 'b', "Bootable", "Toggle bootable flag of the current partition" }, + { 'd', "Delete", "Delete the current partition" }, + { 'g', "Geometry", "Change disk geometry (experts only)" }, + { 'h', "Help", "Print help screen" }, + { 'm', "Maximize", "Maximize disk usage of the current partition (experts only)" }, + { 'n', "New", "Create new partition from free space" }, + { 'p', "Print", "Print partition table to the screen or to a file" }, + { 'q', "Quit", "Quit program without writing partition table" }, + { 't', "Type", "Change the filesystem type (DOS, Linux, OS/2 and so on)" }, + { 'u', "Units", "Change units of the partition size display (MB, sect, cyl)" }, + { 'W', "Write", "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; + + (void)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, 8, + s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0); + } else if (p_info[cur_part].id > 0) { + s = ((opentype == O_RDWR) ? "bdhmpqtuW" : "bdhmpqtu"); + command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, + s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0); + } else { + s = ((opentype == O_RDWR) ? "hpquW" : "hpqu"); + command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, + s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0); + } + switch ( command ) { + case 'B': + case 'b': + if (p_info[cur_part].id > 0) + p_info[cur_part].flags ^= 0x80; + else + print_warning(NO_FLAGS); + 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(DEL_EMPTY); + 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(MAX_UNMAXABLE); + } else + print_warning(MAX_UNMAXABLE); + 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(ADD_UNUSABLE); + else + print_warning(ADD_EXISTS); + 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(TYPE_EMPTY); + break; + case 'U': + case 'u': + if (display_units == MEGABYTES) + display_units = SECTORS; + else if (display_units == SECTORS) + display_units = CYLINDERS; + else if (display_units == CYLINDERS) + display_units = MEGABYTES; + draw_screen(); + break; + case 'W': + write_part_table(); + break; + case 'H': + case 'h': + case '?': + display_help(); + draw_screen(); + break; + case MENU_UP : /* Up arrow */ + if (!draw_cursor(-1)) + command = 0; + else + print_warning(NO_MORE_PARTS); + break; + case MENU_DOWN : /* Down arrow */ + if (!draw_cursor(1)) + command = 0; + else + print_warning(NO_MORE_PARTS); + break; + case REDRAWKEY: + clear(); + draw_screen(); + break; + default: + print_warning(BAD_COMMAND); + putchar(BELL); /* CTRL-G */ + } + } + + die_x(0); +} + +void copyright(void) +{ + fprintf(stderr, "Copyright (C) 1994-1997 Kevin E. Martin & aeb\n"); +} + +void usage(char *prog_name) +{ + fprintf(stderr, "\nUsage:\n"); + fprintf(stderr, "Print version:\n"); + fprintf(stderr, "\t%s -v\n", prog_name); + fprintf(stderr, "Print partition table:\n"); + fprintf(stderr, "\t%s -P {r|s|t} [options] device\n", prog_name); + fprintf(stderr, "Interactive use:\n"); + fprintf(stderr, "\t%s [options] device\n", prog_name); + fprintf(stderr, " +Options: +-a: Use arrow instead of highlighting; +-z: Start with a zero partition table, instead of reading the pt from disk; +-c C -h H -s S: Override the kernel's idea of the number of cylinders, + the number of heads and the number of sectors/track.\n\n"); + + copyright(); +} + +int +main(int argc, char **argv) +{ + int c; + int i, len; + + setlocale(LC_CTYPE, ""); + + while ((c = getopt(argc, argv, "ac:h:s:vzP:")) != EOF) + switch (c) { + case 'a': + arrow_cursor = TRUE; + break; + case 'c': + cylinders = atoi(optarg); + if (cylinders <= 0 || cylinders > MAX_CYLINDERS) { + fprintf(stderr, "%s: %s\n", argv[0], BAD_CYLINDERS); + exit(1); + } + break; + case 'h': + heads = atoi(optarg); + if (heads <= 0 || heads > MAX_HEADS) { + fprintf(stderr, "%s: %s\n", argv[0], BAD_HEADS); + exit(1); + } + break; + case 's': + sectors = atoi(optarg); + if (sectors <= 0 || sectors > MAX_SECTORS) { + fprintf(stderr, "%s: %s\n", argv[0], BAD_SECTORS); + exit(1); + } + break; + case 'v': + fprintf(stderr, "cfdisk %s\n", VERSION); + 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]); + break; + } + } + 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/fdisk/fdisk.8 b/fdisk/fdisk.8 new file mode 100644 index 000000000..6307b98ec --- /dev/null +++ b/fdisk/fdisk.8 @@ -0,0 +1,223 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl) +.\" May be distributed under the GNU General Public License +.TH FDISK 8 "11 June 1998" "Linux 2.0" "Linux Programmer's Manual" +.SH NAME +fdisk \- Partition table manipulator for Linux +.SH SYNOPSIS +.BI "fdisk [\-u] [" device ] +.sp +.BI "fdisk \-l [\-u] [" "device ..." ] +.sp +.BI "fdisk \-s " "partition ..." +.sp +.BI "fdisk \-v +.SH DESCRIPTION +Hard disks can be divided into one or more logical disks called +.IR partitions . +This division is described in the +.I "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. + +.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. + +The +.I device +is usually one of the following: +.br +.nf +.RS +/dev/hda +/dev/hdb +/dev/sda +/dev/sdb +.RE +.fi +(/dev/hd[a-h] for IDE disks, /dev/sd[a-p] for SCSI disks, +/dev/ed[a-d] for ESDI disks, /dev/xd[ab] for XT disks). +A device name refers to the entire disk. + +The +.I partition +is a +.I device +name followed by a partition number. For example, +.B /dev/hda1 +is the first partition on the first IDE hard disk in the system. +IDE disks can have up to 63 partitions, SCSI disks up to 15. +See also +.IR /usr/src/linux/Documentation/devices.txt . + +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 and make some file system 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 different +problems. First of all, these C/H/S fields can be filled only +when the number of heads and the number of sectors per track +are known. Secondly, 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 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 the 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 a BLKRRPART ioctl() (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/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. + +.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 +.B \-v +Print version number of +.B fdisk +program and exit. +.TP +.B \-l +List the partition tables for +.BR /dev/hd[a-d] , +.BR /dev/sd[a-h] , +.BR /dev/ed[a-d] , +and then exit. +.TP +.B \-u +When listing partition tables, give sizes in sectors instead +of cylinders. +.TP +.BI "\-s " partition +The +.I size +of the partition (in blocks) is printed on the standard output. +This value is normally used as an argument to the +.BR mkfs (8) +program to specify the size of the partition which will be formatted. +(Older versions of fdisk would do this only if the partition id is +greater than 10, in an attempt to refuse DOS partitions; +this test has been deleted.) +Note that +.B "sfdisk -s" +gives different (namely, correct) answers. +Reasons for the difference are that the kernel and +.B fdisk +need not have the same idea about partition numbering +(e.g., in case you have BSD slices), and have different +ideas about the size of an extended partition. +.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 . +.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. diff --git a/fdisk/fdisk.c b/fdisk/fdisk.c new file mode 100644 index 000000000..8cdc6df84 --- /dev/null +++ b/fdisk/fdisk.c @@ -0,0 +1,2046 @@ +/* fdisk.c -- Partition table manipulator for Linux. + * + * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) + * + * This program is free software. You can 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. + * + * Before Linux version 0.95c, this program requires a kernel patch. + * + * Modified, Tue Feb 2 18:46:49 1993, faith@cs.unc.edu to better support SCSI. + * Modified, Sat Feb 27 18:11:27 1993, faith@cs.unc.edu: added extfs support. + * Modified, Sat Mar 6 10:14:12 1993, faith@cs.unc.edu: added more comments. + * Modified, Sat Mar 6 12:25:45 1993, faith@cs.unc.edu: + * Added patches from Michael Bischoff (i1041905@ws.rz.tu-bs.de + * or mbi@mo.math.nat.tu-bs.de) to fix the following problems: + * 1) Incorrect mapping of head/sector/cylinder to absolute sector + * 2) Odd sector count causes one sector to be lost + * Modified, Sat Mar 6 12:25:52 1993, faith@cs.unc.edu: improved verification. + * Modified, Sat Apr 17 15:00:00 1993, LeBlanc@mcc.ac.uk: add -s, fix -l. + * Modified, Sat Apr 24 10:00:00 1993, LeBlanc@mcc.ac.uk: fix overlap bug. + * Modified, Wed May 5 21:30:00 1993, LeBlanc@mcc.ac.uk: errors to stderr. + * Modified, Mon Mar 21 20:00:00 1994, LeBlanc@mcc.ac.uk: + * more stderr for messages, avoid division by 0, and + * give reboot message only if ioctl(fd, BLKRRPART) fails. + * Modified, Mon Apr 25 01:01:05 1994, martin@cs.unc.edu: + * 1) Added support for DOS, OS/2, ... compatibility. We should be able + * use this fdisk to partition our drives for other operating systems. + * 2) Added a print the raw data in the partition table command. + * Modified, Wed Jun 22 21:05:30 1994, faith@cs.unc.edu: + * Added/changed a few partition type names to conform to cfdisk. + * (suggested by Sujal, smpatel@wam.umd.edu) + * Modified 3/5/95 leisner@sdsp.mc.xerox.com -- on -l only open + * devices RDONLY (instead of RDWR). This allows you to + * have the disks as rw-r----- with group disk (and if you + * want is safe to setguid fdisk to disk). + * Modified Sat Mar 11 10:02 1995 with more partition types, faith@cs.unc.edu + * Modified, Thu May 4 01:11:45 1995, esr@snark.thyrsus.com: + * It's user-interface cleanup time. + * Actual error messages for out-of-bounds values (what a concept!). + * Enable read-only access to partition table for learners. + * Smart defaults for most numeric prompts. + * Fixed a bug preventing a partition from crossing cylinder 8064, aeb, 950801. + * Read partition table twice to avoid kernel bug + * (from Daniel Quinlan <quinlan@yggdrasil.com>), Tue Sep 26 10:25:28 1995 + * Modified, Sat Jul 1 23:43:16 MET DST 1995, fasten@cs.bonn.edu: + * editor for NetBSD/i386 (and Linux/Alpha?) disklabels. + * Tue Sep 26 17:07:54 1995: More patches from aeb. Fix segfaults, all >4GB. + * Don't destroy random data if extd partition starts past 4GB, aeb, 950818. + * Don't segfault on bad partition created by previous fdisk. + * Fixed for using variable blocksizes (needed by magnet-optical drive-patch) + * (from orschaer@cip.informatik.uni-erlangen.de) + * Modified, Fri Jul 14 11:13:35 MET DST 1996, jj@sunsite.mff.cuni.cz: + * editor for Sun disklabels. + * Modified, Wed Jul 3 10:14:17 MET DST 1996, jj@sunsite.mff.cuni.cz: + * support for Sun floppies + * Modified, Thu Jul 24 16:42:33 MET DST 1997, fasten@shw.com: + * LINUX_EXTENDED support + * Added Windows 95 partition types, aeb. + * Fixed a bug described by johnf@whitsunday.net.au, aeb, 980408. + * [There are lots of other bugs - nobody should use this program] + * [cfdisk on the other hand is nice and correct] + * Try to avoid reading a CD-ROM. + * Do not print Begin column -- it confuses too many people -- aeb, 980610. + * Modified, Sat Oct 3 14:40:17 MET DST 1998, ANeuper@GUUG.de + * Support SGI's partitioning -- an, 980930. + * Do the verify using LBA, not CHS, limits -- aeb, 981206. + */ + + +#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 <getopt.h> + +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <linux/hdreg.h> /* for HDIO_GETGEO */ +#include <linux/fs.h> /* for BLKRRPART, BLKGETSIZE */ + +#include "fdisk.h" + +#include "fdisksunlabel.h" +#include "fdisksgilabel.h" +#include "fdiskaixlabel.h" + +#include "../version.h" + +#define hex_val(c) ({ \ + char _c = (c); \ + isdigit(_c) ? _c - '0' : \ + tolower(_c) + 10 - 'a'; \ + }) + + +#define DEFAULT_DEVICE "/dev/hda" +#define ALTERNATE_DEVICE "/dev/sda" +#define LINE_LENGTH 80 +#define offset(b, n) ((struct partition *)((b) + 0x1be + \ + (n) * sizeof(struct partition))) +#define sector(s) ((s) & 0x3f) +#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) + +#define hsc2sector(h,s,c) (sector(s) - 1 + sectors * \ + ((h) + heads * cylinder(s,c))) +#define set_hsc(h,s,c,sector) { \ + s = sector % sectors + 1; \ + sector /= sectors; \ + h = sector % heads; \ + sector /= heads; \ + c = sector & 0xff; \ + s |= (sector >> 2) & 0xc0; \ + } + +/* A valid partition table sector ends in 0x55 0xaa */ +unsigned int +part_table_flag(char *b) { + return ((uint) b[510]) + (((uint) b[511]) << 8); +} + +int +valid_part_table_flag(unsigned char *b) { + return (b[510] == 0x55 && b[511] == 0xaa); +} + +void +write_part_table_flag(char *b) { + b[510] = 0x55; + b[511] = 0xaa; +} + +/* start_sect and nr_sects are stored little endian on all machines */ +/* moreover, they are not aligned correctly */ +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); +} + +unsigned int +read4_little_endian(unsigned char *cp) { + return (uint)(cp[0]) + ((uint)(cp[1]) << 8) + + ((uint)(cp[2]) << 16) + ((uint)(cp[3]) << 24); +} + +void +set_start_sect(struct partition *p, unsigned int start_sect) { + store4_little_endian(p->start4, start_sect); +} + +unsigned int +get_start_sect(struct partition *p) { + return read4_little_endian(p->start4); +} + +void +set_nr_sects(struct partition *p, unsigned int nr_sects) { + store4_little_endian(p->size4, nr_sects); +} + +unsigned int +get_nr_sects(struct partition *p) { + return read4_little_endian(p->size4); +} + +/* normally O_RDWR, -l option gives O_RDONLY */ +static int type_open = O_RDWR; + +char *disk_device = DEFAULT_DEVICE, /* hda, unless specified */ + *line_ptr, /* interactive input */ + line_buffer[LINE_LENGTH], + changed[MAXIMUM_PARTS], /* marks changed buffers */ + buffer[MAX_SECTOR_SIZE], /* first four partitions */ + *buffers[MAXIMUM_PARTS] /* pointers to buffers */ + = {buffer, buffer, buffer, buffer}; + +int fd, /* the disk */ + ext_index, /* the prime extended partition */ + listing = 0, /* no aborts for fdisk -l */ + nowarn = 0, /* no warnings for fdisk -l/-s */ + dos_compatible_flag = ~0, + partitions = 4; /* maximum partition + 1 */ + +uint heads, + sectors, + cylinders, + sector_size = DEFAULT_SECTOR_SIZE, + sector_offset = 1, + display_factor = 1, /* in units/sector */ + unit_flag = 1, + extended_offset = 0, /* offset of link pointers */ + offsets[MAXIMUM_PARTS] = {0, 0, 0, 0}; + +int sun_label = 0; /* looking at sun disklabel */ +int sgi_label = 0; /* looking at sgi disklabel */ +int aix_label = 0; /* looking at aix disklabel */ + +struct partition *part_table[MAXIMUM_PARTS] /* partitions */ + = {offset(buffer, 0), offset(buffer, 1), + offset(buffer, 2), offset(buffer, 3)}, + *ext_pointers[MAXIMUM_PARTS] /* link pointers */ + = {NULL, NULL, NULL, NULL}; + +struct systypes sys_types[] = { + {0x00, "Empty"}, + {0x01, "DOS 12-bit FAT"}, + {0x02, "XENIX root"}, + {0x03, "XENIX usr"}, + {0x04, "DOS 16-bit <32M"}, + {0x05, "Extended"}, + {0x06, "DOS 16-bit >=32M"}, + {0x07, "OS/2 HPFS"}, /* or QNX? */ + {0x08, "AIX"}, + {0x09, "AIX bootable"}, + {0x0a, "OS/2 Boot Manager"}, + {0x0b, "Win95 FAT32"}, + {0x0c, "Win95 FAT32 (LBA)"}, + {0x0e, "Win95 FAT16 (LBA)"}, + {0x0f, "Win95 Extended (LBA)"}, + {0x11, "Hidden DOS FAT12"}, + {0x14, "Hidden DOS FAT16"}, + {0x16, "Hidden DOS FAT16 (big)"}, + {0x17, "Hidden OS/2 HPFS or NTFS"}, + {0x40, "Venix 80286"}, + {0x41, "PPC PReP Boot"}, + {0x51, "Novell?"}, + {0x52, "Microport"}, /* or CPM? */ + {0x63, "GNU HURD"}, /* or System V/386? */ + {0x64, "Novell Netware 286"}, + {0x65, "Novell Netware 386"}, + {0x75, "PC/IX"}, + {0x80, "Old MINIX"}, /* Minix 1.4a and earlier */ + + {LINUX_PARTITION, "Linux/MINIX"}, /* Minix 1.4b and later */ + {LINUX_SWAP, "Linux swap"}, + {LINUX_NATIVE, "Linux native"}, + {LINUX_EXTENDED, "Linux extended"}, + + {0x93, "Amoeba"}, + {0x94, "Amoeba BBT"}, /* (bad block table) */ + {0xa5, "BSD/386"}, + {0xa6, "OpenBSD"}, + {0xa7, "NEXTSTEP"}, + {0xb7, "BSDI fs"}, + {0xb8, "BSDI swap"}, + {0xc7, "Syrinx"}, + {0xdb, "CP/M"}, /* or Concurrent DOS? */ + {0xe1, "DOS access"}, + {0xe3, "DOS R/O"}, + {0xeb, "BeOS fs"}, + {0xf2, "DOS secondary"}, + {0xff, "BBT"}, /* (bad track table) */ + { 0, NULL } +}; + +jmp_buf listingbuf; + +void fatal(enum failure why) +{ + char error[LINE_LENGTH], + *message = error; + + if (listing) { + close(fd); + longjmp(listingbuf, 1); + } + + switch (why) { + case usage: message = +"Usage: fdisk [-b SSZ] [-u] [DISK] Change partition table\n" +" fdisk -l [-b SSZ] [-u] [DISK] List partition table(s)\n" +" fdisk -s PARTITION Give partition size(s) in blocks\n" +" fdisk -v Give fdisk version\n" +"Here DISK is something like /dev/hdb or /dev/sda\n" +"and PARTITION is something like /dev/hda7\n" +"-u: give Start and End in sector (instead of cylinder) units\n" +"-b 2048: (for certain MO drives) use 2048-byte sectors\n"; + break; + case no_device: + message = "A disk block device is needed.\n"; + break; + case no_partition: + message = "Given name does not refer to a partition,\n" + "or maybe not even to a block device.\n"; + break; + case unable_to_open: + sprintf(error, "Unable to open %s\n", disk_device); + break; + case unable_to_read: + sprintf(error, "Unable to read %s\n", disk_device); + break; + case unable_to_seek: + sprintf(error, "Unable to seek on %s\n", disk_device); + break; + case unable_to_write: + sprintf(error, "Unable to write %s\n", disk_device); + break; + case out_of_memory: + message = "Unable to allocate any more memory\n"; + break; + default: message = "Fatal error\n"; + } + + fputc('\n', stderr); + fputs(message, stderr); + exit(1); +} + +void menu(void) +{ + if (sun_label) + puts("Command action\n" + " a toggle a read only flag\n" /* sun */ + " b edit bsd disklabel\n" + " c toggle the mountable flag\n" /* sun */ + " d delete a partition\n" + " l list known partition types\n" + " m print this menu\n" + " n add a new partition\n" + " o create a new empty DOS partition table\n" + " p print the partition table\n" + " q quit without saving changes\n" + " s create a new empty Sun disklabel\n" /* sun */ + " t change a partition's system id\n" + " u change display/entry units\n" + " v verify the partition table\n" + " w write table to disk and exit\n" + " x extra functionality (experts only)" + ); + else if(sgi_label) + puts("Command action\n" + " a select bootable partition\n" /* sgi flavour */ + " b edit bootfile entry\n" /* sgi */ + " c select sgi swap partition\n" /* sgi flavour */ + " d delete a partition\n" + " l list known partition types\n" + " m print this menu\n" + " n add a new partition\n" + " o create a new empty DOS partition table\n" + " p print the partition table\n" + " q quit without saving changes\n" + " t change a partition's system id\n" + " u change display/entry units\n" + " v verify the partition table\n" + " w write table to disk and exit\n" + ); + else if(aix_label) + puts("Command action\n" + " m print this menu\n" + " o create a new empty DOS partition table\n" + " q quit without saving changes\n" + ); + else + puts("Command action\n" + " a toggle a bootable flag\n" + " b edit bsd disklabel\n" + " c toggle the dos compatibility flag\n" + " d delete a partition\n" + " l list known partition types\n" + " m print this menu\n" + " n add a new partition\n" + " o create a new empty DOS partition table\n" + " p print the partition table\n" + " q quit without saving changes\n" + " t change a partition's system id\n" + " u change display/entry units\n" + " v verify the partition table\n" + " w write table to disk and exit\n" + " x extra functionality (experts only)" + ); +} + +void xmenu(void) +{ + if (sun_label) + puts("Command action\n" + " a change number of alternate cylinders\n" /* sun */ + " c change number of cylinders\n" + " d print the raw data in the partition table\n" + " e change number of extra sectors per cylinder\n" /*sun*/ + " h change number of heads\n" + " i change interleave factor\n" /* sun */ + " o change rotation speed (rpm)\n" /* sun */ + " m print this menu\n" + " p print the partition table\n" + " q quit without saving changes\n" + " r return to main menu\n" + " s change number of sectors\n" + " v verify the partition table\n" + " w write table to disk and exit\n" + " y change number of physical cylinders" /* sun */ + ); + else + puts("Command action\n" + " b move beginning of data in a partition\n" /* !sun */ + " c change number of cylinders\n" + " d print the raw data in the partition table\n" + " e list extended partitions\n" /* !sun */ + " g create an IRIX partition table\n" /* sgi */ + " h change number of heads\n" + " m print this menu\n" + " p print the partition table\n" + " q quit without saving changes\n" + " r return to main menu\n" + " s change number of sectors\n" + " v verify the partition table\n" + " w write table to disk and exit" + ); +} + +int +get_sysid(int i) { + return ( + sun_label ? sunlabel->infos[i].id : + sgi_label ? sgi_get_sysid(i) : part_table[i]->sys_ind); +} + +struct systypes * +get_sys_types(void) { + return ( + sun_label ? sun_sys_types : + sgi_label ? sgi_sys_types : 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].index == type) + return types[i].name; + + return NULL; +} + +void list_types(struct systypes *sys) +{ + uint 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 { + printf("%c%2x %-15.15s", i ? ' ' : '\n', + sys[next].index, sys[next].name); + next = last[i++] + done; + if (i > 3 || next >= last[i]) { + i = 0; + next = ++done; + } + } while (done < last[0]); + putchar('\n'); +} + +void clear_partition(struct partition *p) +{ + 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 set_partition(int i, struct partition *p, uint start, uint stop, + int sysid, uint offset) +{ + p->boot_ind = 0; + p->sys_ind = sysid; + set_start_sect(p, start - offset); + set_nr_sects(p, stop - start + 1); + if (dos_compatible_flag && (start/(sectors*heads) > 1023)) + start = heads*sectors*1024 - 1; + set_hsc(p->head, p->sector, p->cyl, start); + if (dos_compatible_flag && (stop/(sectors*heads) > 1023)) + stop = heads*sectors*1024 - 1; + set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); + changed[i] = 1; +} + +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; +} + +int warn_geometry(void) +{ + char *m = NULL; + int prev = 0; + if (!heads) + prev = test_c(&m, "heads"); + if (!sectors) + prev = test_c(&m, "sectors"); + if (!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(void) +{ + int cyl_units = heads * sectors; + + if (unit_flag && cyl_units) + display_factor = cyl_units; + else + display_factor = 1; /* in sectors */ +} + +void warn_cylinders(void) +{ + if (!sun_label && !sgi_label && cylinders > 1024 && !nowarn) + fprintf(stderr, "\n\ +The number of cylinders for this disk is set to %d.\n\ +There is nothing wrong with that, but this is larger than 1024,\n\ +and could in certain setups cause problems with:\n\ +1) software that runs at boot time (e.g., LILO)\n\ +2) booting and partitioning software from other OSs\n\ + (e.g., DOS FDISK, OS/2 FDISK)\n", + cylinders); +} + +void read_extended(struct partition *p) +{ + int i; + struct partition *q; + + ext_pointers[ext_index] = part_table[ext_index]; + if (!get_start_sect(p)) + fprintf(stderr, "Bad offset in primary extended partition\n"); + else while (IS_EXTENDED (p->sys_ind)) { + if (partitions >= MAXIMUM_PARTS) { + fprintf(stderr, + "Warning: deleting partitions after %d\n", + partitions); + clear_partition(ext_pointers[partitions - 1]); + changed[partitions - 1] = 1; + return; + } + offsets[partitions] = extended_offset + get_start_sect(p); + if (!extended_offset) + extended_offset = get_start_sect(p); + if (ext2_llseek(fd, (ext2_loff_t)offsets[partitions] + * sector_size, SEEK_SET) < 0) + fatal(unable_to_seek); + if (!(buffers[partitions] = (char *) malloc(sector_size))) + fatal(out_of_memory); + if (sector_size != read(fd, buffers[partitions], sector_size)) + fatal(unable_to_read); + part_table[partitions] = ext_pointers[partitions] = NULL; + q = p = offset(buffers[partitions], 0); + for (i = 0; i < 4; i++, p++) { + if (IS_EXTENDED (p->sys_ind)) { + if (ext_pointers[partitions]) + fprintf(stderr, "Warning: extra link " + "pointer in partition table " + "%d\n", partitions + 1); + else + ext_pointers[partitions] = p; + } else if (p->sys_ind) { + if (part_table[partitions]) + fprintf(stderr, + "Warning: ignoring extra data " + "in partition table %d\n", + partitions + 1); + else + part_table[partitions] = p; + } + } + if (!part_table[partitions]) { + if (q != ext_pointers[partitions]) + part_table[partitions] = q; + else part_table[partitions] = q + 1; + } + if (!ext_pointers[partitions]) { + if (q != part_table[partitions]) + ext_pointers[partitions] = q; + else ext_pointers[partitions] = q + 1; + } + p = ext_pointers[partitions++]; + } +} + +void create_doslabel(void) +{ + int i; + + fprintf(stderr, + "Building a new DOS disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content won't be recoverable.\n\n"); + + sun_nolabel(); /* otherwise always recognised as sun */ + sgi_nolabel(); /* otherwise always recognised as sgi */ + + write_part_table_flag(buffer); + for (i = 0; i < 4; i++) + clear_partition(part_table[i]); + for (i = 1; i < MAXIMUM_PARTS; i++) + changed[i] = 0; + changed[0] = 1; + get_boot(create_empty); +} + +/* + * Read MBR. Returns: + * -1: no 0xaa55 flag present (possibly entire disk BSD) + * 0: found or created label + */ +int get_boot(enum action what) +{ + int i, sec_fac; + struct hd_geometry geometry; + + partitions = 4; + sec_fac = sector_size / 512; + + if (what == create_empty) + goto got_table; /* skip reading disk */ + + if ((fd = open(disk_device, type_open)) < 0) { + if ((fd = open(disk_device, O_RDONLY)) < 0) + fatal(unable_to_open); + else + printf("You will not be able to write the partition table.\n"); + } + + guess_device_type(fd); + + if (sector_size != read(fd, buffer, sector_size)) + fatal(unable_to_read); + +#ifdef HDIO_REQ + if (!ioctl(fd, HDIO_REQ, &geometry)) { +#else + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { +#endif + heads = geometry.heads; + sectors = geometry.sectors; + cylinders = geometry.cylinders; + cylinders /= sec_fac; /* do not round up */ + if (dos_compatible_flag) + sector_offset = sectors; + } else { + if (!ioctl(fd, BLKGETSIZE, §ors)) { + heads = 1; + cylinders = 1; + sectors /= sec_fac; + } else { + heads = cylinders = sectors = 0; + } + } + update_units(); + +got_table: + + if (check_sun_label()) + return 0; + + if (check_sgi_label()) + return 0; + + if (check_aix_label()) + return 0; + + if (!valid_part_table_flag(buffer)) { + switch(what) { + case fdisk: + fprintf(stderr, + "Device contains neither a valid DOS partition" + " table, nor Sun or SGI disklabel\n"); +#ifdef __sparc__ + create_sunlabel(); +#else + create_doslabel(); +#endif + return 0; + case require: + return -1; + case try_only: + return -1; + case create_empty: + break; + } + + fprintf(stderr, "Internal error\n"); + exit(1); + } + + warn_cylinders(); + warn_geometry(); + + for (i = 0; i < 4; i++) + if(IS_EXTENDED (part_table[i]->sys_ind)) { + if (partitions != 4) + fprintf(stderr, "Ignoring extra extended " + "partition %d\n", i + 1); + else read_extended(part_table[ext_index = i]); + } + for (i = 3; i < partitions; i++) + if (!valid_part_table_flag(buffers[i])) { + fprintf(stderr, + "Warning: invalid flag 0x%04x of partition " + "table %d will be corrected by w(rite)\n", + part_table_flag(buffers[i]), i + 1); + changed[i] = 1; + } + + return 0; +} + +/* read line; return 0 or first char */ +int +read_line(void) +{ + line_ptr = line_buffer; + if (!fgets(line_buffer, LINE_LENGTH, stdin)) + return 0; + while (*line_ptr && !isgraph(*line_ptr)) + line_ptr++; + return *line_ptr; +} + +char +read_char(char *mesg) +{ + do { + fputs(mesg, stdout); + } while (!read_line()); + return *line_ptr; +} + +char +read_chars(char *mesg) +{ + fputs(mesg, stdout); + if (!read_line()) { + *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; + } + } +} + +/* + * Print the message MESG, then read an integer between LOW and HIGH. + * If the user hits Enter, DFLT is returned. + * Answers like +10 are interpreted as offsets from BASE. + * + * There is no default if DFLT is not between LOW and HIGH. + */ +uint +read_int(uint low, uint dflt, uint high, uint base, char *mesg) +{ + uint i; + int default_ok = 1; + static char *ms = NULL; + static int mslen = 0; + + if (!ms || strlen(mesg)+50 > mslen) { + mslen = strlen(mesg)+100; + if (!(ms = realloc(ms,mslen))) + fatal(out_of_memory); + } + + if (dflt < low || dflt > high) + default_ok = 0; + + if (default_ok) + sprintf(ms, "%s (%d-%d, default %d): ", mesg, low, high, dflt); + else + sprintf(ms, "%s (%d-%d): ", 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 == '-') { + i = atoi(line_ptr+1); + if (*line_ptr == '-') + i = -i; + while (isdigit(*++line_ptr)) + use_default = 0; + switch (*line_ptr) { + case 'c': + case 'C': + if (!unit_flag) + i *= heads * sectors; + break; + case 'k': + case 'K': + i *= 2; + i /= (sector_size / 512); + i /= display_factor; + break; + case 'm': + case 'M': + i *= 2048; + i /= (sector_size / 512); + i /= display_factor; + break; + case 'g': + case 'G': + i *= 2048000; + i /= (sector_size / 512); + i /= display_factor; + break; + default: + break; + } + i += base; + } else { + i = atoi(line_ptr); + while (isdigit(*line_ptr)) { + line_ptr++; + use_default = 0; + } + } + if (use_default) + printf("Using default value %d\n", i = dflt); + if (i >= low && i <= high) + break; + else + printf("Value out of range.\n"); + } + return i; +} + +int get_partition(int warn, int max) +{ + int i = read_int(1, 0, max, 0, "Partition number") - 1; + + if (warn && ( + (!sun_label && !sgi_label && !part_table[i]->sys_ind) + || (sun_label && + (!sunlabel->partitions[i].num_sectors || + !sunlabel->infos[i].id)) + || (sgi_label && (!sgi_get_num_sectors(i))) + )) fprintf(stderr, "Warning: partition %d has empty type\n", i+1); + return i; +} + +char *const str_units(void) +{ + return unit_flag ? "cylinder" : "sector"; +} + +void change_units(void) +{ + if ((unit_flag = !unit_flag)) + display_factor = 1; + else display_factor = heads * sectors; + update_units(); + printf("Changing display/entry units to %ss\n", + str_units()); +} + +void toggle_active(int i) +{ + struct partition *p = part_table[i]; + + if (IS_EXTENDED (p->sys_ind) && !p->boot_ind) + fprintf(stderr, + "WARNING: Partition %d is an extended partition\n", + i + 1); + if (p->boot_ind) + p->boot_ind = 0; + else p->boot_ind = ACTIVE_FLAG; + changed[i] = 1; +} + +void toggle_dos(void) +{ + dos_compatible_flag = ~dos_compatible_flag; + printf("DOS Compatibility flag is "); + if (dos_compatible_flag) + sector_offset = sectors; + else { + sector_offset = 1; + printf("not "); + } + printf("set\n"); +} + +void delete_partition(int i) +{ + struct partition *p = part_table[i], *q = ext_pointers[i]; + +/* Note that for the fifth partition (i == 4) we don't actually + * decrement partitions. + */ + + if (warn_geometry()) + return; + changed[i] = 1; + + if (sun_label) { + sun_delete_partition(i); + return; + } + if (sgi_label) { + sgi_delete_partition(i); + return; + } + if (i < 4) { + if (IS_EXTENDED (p->sys_ind) && i == ext_index) { + while (partitions > 4) + free(buffers[--partitions]); + ext_pointers[ext_index] = NULL; + extended_offset = 0; + } + clear_partition(p); + } + else if (!q->sys_ind && i > 4) { + free(buffers[--partitions]); + clear_partition(ext_pointers[--i]); + } + else if (i > 3) { + if (i > 4) { + p = ext_pointers[i - 1]; + p->boot_ind = 0; + p->head = q->head; + p->sector = q->sector; + p->cyl = q->cyl; + p->sys_ind = EXTENDED; + p->end_head = q->end_head; + p->end_sector = q->end_sector; + p->end_cyl = q->end_cyl; + set_start_sect(p, get_start_sect(q)); + set_nr_sects(p, get_nr_sects(q)); + changed[i - 1] = 1; + } else { + if(part_table[5]) /* prevent SEGFAULT */ + set_start_sect(part_table[5], + get_start_sect(part_table[5]) + + offsets[5] - extended_offset); + offsets[5] = extended_offset; + changed[5] = 1; + } + if (partitions > 5) { + partitions--; + free(buffers[i]); + while (i < partitions) { + changed[i] = changed[i + 1]; + buffers[i] = buffers[i + 1]; + offsets[i] = offsets[i + 1]; + part_table[i] = part_table[i + 1]; + ext_pointers[i] = ext_pointers[i + 1]; + i++; + } + } + else + clear_partition(part_table[i]); + } +} + +void change_sysid(void) +{ + char *temp; + int i = get_partition(0, partitions), sys, origsys; + struct partition *p = part_table[i]; + + origsys = sys = get_sysid(i); + + if (!sys && !sgi_label) + printf("Partition %d does not exist yet!\n", i + 1); + else while (1) { + sys = read_hex (get_sys_types()); + + if (!sys && !sgi_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 (!sun_label && !sgi_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 (sun_label && i == 2 && sys != WHOLE_DISK) + printf("Consider leaving partition 3 " + "as Whole disk (5),\n" + "as SunOS/Solaris expects it and " + "even Linux likes it.\n\n"); + if (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 (sun_label) { + sun_change_sysid(i, sys); + } else + if (sgi_label) { + sgi_change_sysid(i, sys); + } else + part_table[i]->sys_ind = sys; + printf ("Changed system type of partition %d " + "to %x (%s)\n", i + 1, sys, + (temp = partition_type(sys)) ? temp : + "Unknown"); + changed[i] = 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(ulong ls, uint *c, uint *h, uint *s) +{ + int spc = heads * sectors; + + *c = ls / spc; + ls = ls % spc; + *h = ls / sectors; + *s = ls % sectors + 1; /* sectors count from 1 */ +} + +static void check_consistency(struct partition *p, int partition) +{ + uint pbc, pbh, pbs; /* physical beginning c, h, s */ + uint pec, peh, pes; /* physical ending c, h, s */ + uint lbc, lbh, lbs; /* logical beginning c, h, s */ + uint lec, leh, les; /* logical ending c, h, s */ + + if (!heads || !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(get_start_sect(p), &lbc, &lbh, &lbs); + +/* compute logical ending (c, h, s) */ + long2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les); + +/* Same physical / logical beginning? */ + if (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 (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); + } + +#if 0 +/* Beginning on cylinder boundary? */ + if (pbh != !pbc || pbs != 1) { + printf("Partition %i does not start on cylinder " + "boundary:\n", partition + 1); + printf(" phys=(%d, %d, %d) ", pbc, pbh, pbs); + printf("should be (%d, %d, 1)\n", pbc, !pbc); + } +#endif + +/* Ending on cylinder boundary? */ + if (peh != (heads - 1) || pes != sectors) { + printf("Partition %i does not end on cylinder boundary:\n", + partition + 1); + printf(" phys=(%d, %d, %d) ", pec, peh, pes); + printf("should be (%d, %d, %d)\n", + pec, heads - 1, sectors); + } +} + +void list_disk_geometry(void) +{ + printf("\nDisk %s: %d heads, %d sectors, %d cylinders\nUnits = " + "%ss of %d * %d bytes\n\n", disk_device, heads, sectors, + cylinders, str_units(), display_factor, sector_size); +} + +void list_table(int xtra) +{ + struct partition *p; + char *type; + int digit_last = 0; + int i, w; + + if (sun_label) { + sun_list_table(xtra); + return; + } + + if (sgi_label) { + sgi_list_table(xtra); + return; + } + + w = strlen(disk_device); + /* 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. */ + if (isdigit(disk_device[w-1])) + digit_last = 1; + + list_disk_geometry(); + + if (w < 5) + w = 5; + printf("%*s Boot Start End Blocks Id System\n", + (digit_last ? w + 2 : w + 1), "Device"); + + for (i = 0 ; i < partitions; i++) { + if ((p = part_table[i])->sys_ind) { + unsigned int psects = get_nr_sects(p); + unsigned int pblocks = psects; + unsigned int podd = 0; + + if (sector_size < 1024) { + pblocks /= (1024 / sector_size); + podd = psects % (1024 / sector_size); + } + if (sector_size > 1024) + pblocks *= (sector_size / 1024); + printf( + "%*s%s%-2d %c %9ld %9ld %9ld%c %2x %s\n", +/* device */ w, disk_device, (digit_last ? "p" : ""), i+1, +/* boot flag */ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG + ? '*' : '?', +/* start */ (long) cround(get_start_sect(p) + offsets[i]), +/* end */ (long) cround(get_start_sect(p) + offsets[i] + psects + - (psects ? 1 : 0)), +/* odd flag on end */ (long) pblocks, podd ? '+' : ' ', +/* type id */ p->sys_ind, +/* type name */ (type = partition_type(p->sys_ind)) ? + type : "Unknown"); + check_consistency(p, i); + } + } +} + +void x_list_table(int extend) +{ + struct partition *p, **q; + int i; + + if (extend) + q = ext_pointers; + else + q = part_table; + printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n", + disk_device, heads, sectors, cylinders); + printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"); + for (i = 0 ; i < partitions; i++) + if ((p = q[i]) != NULL) { + printf("%2d %02x%4d%4d%5d%4d%4d%5d%8d%8d %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), + get_start_sect(p), get_nr_sects(p), p->sys_ind); + if (p->sys_ind) + check_consistency(p, i); + } +} + +void fill_bounds(uint *first, uint *last) +{ + int i; + struct partition *p = part_table[0]; + + for (i = 0; i < partitions; p = part_table[++i]) { + if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) { + first[i] = 0xffffffff; + last[i] = 0; + } else { + first[i] = get_start_sect(p) + offsets[i]; + last[i] = first[i] + get_nr_sects(p) - 1; + } + } +} + +void check(int n, uint h, uint s, uint c, uint start) +{ + uint total, real_s, real_c; + + real_s = sector(s) - 1; + real_c = cylinder(s, c); + total = (real_c * sectors + real_s) * heads + h; + if (!total) + fprintf(stderr, "Warning: partition %d contains sector 0\n", n); + if (h >= heads) + fprintf(stderr, + "Partition %d: head %d greater than maximum %d\n", + n, h + 1, heads); + if (real_s >= sectors) + fprintf(stderr, "Partition %d: sector %d greater than " + "maximum %d\n", n, s, sectors); + if (real_c >= cylinders) + fprintf(stderr, "Partitions %d: cylinder %d greater than " + "maximum %d\n", n, real_c + 1, cylinders); + if (cylinders <= 1024 && start != total) + fprintf(stderr, + "Partition %d: previous sectors %d disagrees with " + "total %d\n", n, start, total); +} + + +void verify(void) +{ + int i, j; + uint total = 1; + uint first[partitions], last[partitions]; + struct partition *p = part_table[0]; + + if (warn_geometry()) + return; + + if (sun_label) { + verify_sun(); + return; + } + + if (sgi_label) { + verify_sgi(1); + return; + } + + fill_bounds(first, last); + for (i = 0; i < partitions; p = part_table[++i]) + if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) { + check_consistency(p, i); + if (get_start_sect(p) + offsets[i] < first[i]) + printf("Warning: bad start-of-data in " + "partition %d\n", i + 1); + check(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) { + uint e_last = get_start_sect(part_table[ext_index]) + + get_nr_sects(part_table[ext_index]) - 1; + + for (p = part_table[i = 4]; i < partitions; + p = part_table[++i]) { + total++; + 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 > heads * sectors * cylinders) + printf("Total allocated sectors %d greater than the maximum " + "%d\n", total, heads * sectors * cylinders); + else if ((total = heads * sectors * cylinders - total) != 0) + printf("%d unallocated sectors\n", total); +} + +void add_partition(int n, int sys) +{ + char mesg[48]; + int i, read = 0; + struct partition *p = part_table[n], *q = part_table[ext_index]; + uint start, stop = 0, limit, temp, + first[partitions], last[partitions]; + + if (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; + limit = heads * sectors * cylinders - 1; + 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 (unit_flag) + for (i = 0; i < partitions; i++) + first[i] = (cround(first[i]) - 1) * display_factor; + + sprintf(mesg, "First %s", str_units()); + do { + temp = start; + for (i = 0; i < partitions; i++) { + int lastplusoff; + + if (start == offsets[i]) + start += sector_offset; + lastplusoff = last[i] + ((n<4) ? 0 : sector_offset); + if (start >= first[i] && start <= lastplusoff) + start = lastplusoff + 1; + } + if (start > limit) + break; + if (start >= temp+display_factor && read) { + printf("Sector %d is already allocated\n", temp); + temp = start; + read = 0; + } + if (!read && start == temp) { + uint i; + i = start; + start = read_int(cround(i), cround(i), cround(limit), + 0, mesg); + if (unit_flag) { + start = (start - 1) * display_factor; + if (start < i) start = i; + } + read = 1; + } + } while (start != temp || !read); + if (n > 4) /* NOT for fifth partition */ + offsets[n] = start - sector_offset; + + for (i = 0; i < partitions; i++) { + if (start < offsets[i] && limit >= offsets[i]) + limit = offsets[i] - 1; + if (start < first[i] && limit >= first[i]) + limit = first[i] - 1; + } + if (start > limit) { + printf("No free sectors available\n"); + if (n > 4) { + free(buffers[n]); + partitions--; + } + return; + } + if (cround(start) == cround(limit)) + stop = start; + else { + sprintf(mesg, "Last %s or +size or +sizeM or +sizeK", + str_units()); + stop = read_int(cround(start), cround(limit), cround(limit), + cround(start), mesg); + if (unit_flag) { + stop = stop * display_factor - 1; + if (stop >limit) + stop = limit; + } + } + + set_partition(n, p, start, stop, sys, offsets[n]); + + if (IS_EXTENDED (sys)) { + ext_index = n; + offsets[4] = extended_offset = start; + ext_pointers[n] = p; + if (!(buffers[4] = calloc(1, sector_size))) + fatal(out_of_memory); + part_table[4] = offset(buffers[4], 0); + ext_pointers[4] = part_table[4] + 1; + changed[4] = 1; + partitions = 5; + } + else { + if (n > 4) + set_partition(n - 1, ext_pointers[n - 1], + start - sector_offset, stop, EXTENDED, + extended_offset); +#if 0 + if ((limit = get_nr_sects(p)) & 1) + printf("Warning: partition %d has an odd " + "number of sectors.\n", n + 1); +#endif + } +} + +void add_logical(void) +{ + if (partitions > 5 || part_table[4]->sys_ind) { + if (!(buffers[partitions] = calloc(1, sector_size))) + fatal(out_of_memory); + part_table[partitions] = offset(buffers[partitions], 0); + ext_pointers[partitions] = part_table[partitions] + 1; + offsets[partitions] = 0; + partitions++; + } + add_partition(partitions - 1, LINUX_NATIVE); +} + +void new_partition(void) +{ + int i, free_primary = 0; + + if (warn_geometry()) + return; + + if (sun_label) { + add_sun_partition(get_partition(0, partitions), LINUX_NATIVE); + return; + } + + if (sgi_label) { + sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE); + return; + } + + if (partitions >= MAXIMUM_PARTS) { + printf("The maximum number of partitions has been created\n"); + return; + } + + for (i = 0; i < 4; i++) + free_primary += !part_table[i]->sys_ind; + if (!free_primary) { + if (extended_offset) + add_logical(); + else + printf("You must delete some partition and add " + "an extended partition first\n"); + } else { + char c, line[LINE_LENGTH]; + sprintf(line, "Command action\n %s\n p primary " + "partition (1-4)\n", extended_offset ? + "l logical (5 or over)" : "e extended"); + while (1) + if ((c = tolower(read_char(line))) == 'p') { + add_partition(get_partition(0, 4), + LINUX_NATIVE); + return; + } + else if (c == 'l' && extended_offset) { + add_logical(); + return; + } + else if (c == 'e' && !extended_offset) { + add_partition(get_partition(0, 4), + EXTENDED); + return; + } + else + printf("Invalid partition number " + "for type `%c'\n", c); + + } +} + +void write_table(void) +{ + int i, error = 0; + + changed[3] = changed[0] || changed[1] || changed[2] || changed[3]; + if (!sun_label && !sgi_label) { + for (i = 3; i < partitions; i++) { + if (changed[i]) { + write_part_table_flag(buffers[i]); + if (ext2_llseek(fd, (ext2_loff_t)offsets[i] + * sector_size, SEEK_SET) < 0) + fatal(unable_to_seek); + if (write(fd, buffers[i], sector_size) != sector_size) + fatal(unable_to_write); + } + } + } else if (sgi_label) { + /* no test on change? the printf below might be mistaken */ + sgi_write_table(); + } else if (sun_label) { + if (changed[3] || changed[4] || changed[5] || + changed[6] || changed[7]) { + sun_write_table(); + } + } + + printf("The partition table has been altered!\n\n"); + + printf("Calling ioctl() to re-read partition table.\n"); + sync(); + sleep(2); + if ((i = ioctl(fd, BLKRRPART)) != 0) { + error = errno; + } else { + /* some kernel versions (1.2.x) seem to have trouble + rereading the partition table, but if asked to do it + twice, the second time works. - biro@yggdrasil.com */ + sync(); + sleep(2); + if((i = ioctl(fd, BLKRRPART)) != 0) + error = errno; + } + + close(fd); + + printf("Syncing disks.\n"); + sync(); + sleep(4); /* for sync() */ + + if (i < 0) + printf("Re-read table failed with error %d: %s.\nReboot your " + "system to ensure the partition table is updated.\n", + error, strerror(error)); + + if (!sun_label && !sgi_label) + 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"); + + exit(0); +} + +#define MAX_PER_LINE 16 +void print_buffer(char buffer[]) +{ + int i, + l; + + for (i = 0, l = 0; i < sector_size; i++, l++) { + if (l == 0) + printf("0x%03X:", i); + printf(" %02X", (unsigned char) buffer[i]); + if (l == MAX_PER_LINE - 1) { + printf("\n"); + l = -1; + } + } + if (l > 0) + printf("\n"); + printf("\n"); +} + +void print_raw(void) +{ + int i; + + printf("Device: %s\n", disk_device); + if (sun_label || sgi_label) + print_buffer(buffer); + else for (i = 3; i < partitions; i++) + print_buffer(buffers[i]); +} + +void move_begin(int i) +{ + struct partition *p = part_table[i]; + uint new, first; + + if (warn_geometry()) + 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; + } + first = get_start_sect(p) + offsets[i]; + new = read_int(first, first, + get_start_sect(p) + get_nr_sects(p) + offsets[i] - 1, + first, "New beginning of data") - offsets[i]; + + if (new != get_nr_sects(p)) { + first = get_nr_sects(p) + get_start_sect(p) - new; + set_nr_sects(p, first); + set_start_sect(p, new); + changed[i] = 1; + } +} + +void xselect(void) +{ + while(1) { + putchar('\n'); + switch (tolower(read_char("Expert command (m for help): "))) { + case 'a': + if (sun_label) + sun_set_alt_cyl(); + break; + case 'b': + if (!sun_label && !sgi_label) + move_begin(get_partition(0, partitions)); + break; + case 'c': + cylinders = read_int(1, cylinders, 65535, + 0, "Number of cylinders"); + if (sun_label) + sun_set_ncyl(cylinders); + warn_cylinders(); + break; + case 'd': + print_raw(); + break; + case 'e': + if (sgi_label) + sgi_set_xcyl(); + else if (sun_label) + sun_set_xcyl(); + else + x_list_table(1); + break; + case 'g': + create_sgilabel(); + break; + case 'h': + heads = read_int(1, heads, 256, 0, + "Number of heads"); + update_units(); + break; + case 'i': + if (sun_label) + sun_set_ilfact(); + break; + case 'o': + if (sun_label) + sun_set_rspeed(); + break; + case 'p': + if (sun_label) + list_table(1); + else + x_list_table(0); + break; + case 'q': + close(fd); + exit(0); + case 'r': + return; + case 's': + sectors = read_int(1, sectors, 63, 0, + "Number of sectors"); + if (dos_compatible_flag) { + sector_offset = sectors; + fprintf(stderr, "Warning: setting " + "sector offset for DOS " + "compatiblity\n"); + } + update_units(); + break; + case 'v': + verify(); + break; + case 'w': + write_table(); /* does not return */ + break; + case 'y': + if (sun_label) + sun_set_pcylcount(); + break; + default: + xmenu(); + } + } +} + +int +is_ide_cdrom(char *device) { + /* No device was given explicitly, and we are trying some + likely things. But opening /dev/hdc may produce errors like + "hdc: tray open or drive not ready" + if it happens to be a CD-ROM drive. It even happens that + the process hangs on the attempt to read a music CD. + So try to be careful. This only works since 2.1.73. */ + + FILE *procf; + char buf[100]; + struct stat statbuf; + + if (strncmp("/dev/hd", device, 7)) + return 0; + sprintf(buf, "/proc/ide/%s/media", device+5); + procf = fopen(buf, "r"); + if (procf != NULL && fgets(buf, sizeof(buf), procf)) + return !strncmp(buf, "cdrom", 5); + + /* Now when this proc file does not exist, skip the + device when it is read-only. */ + if (stat(device, &statbuf) == 0) + return (statbuf.st_mode & 0222) == 0; + + return 0; +} + +void try(char *device, int user_specified) +{ + disk_device = device; + if (!setjmp(listingbuf)) { + if (!user_specified) + if (is_ide_cdrom(device)) + return; + if ((fd = open(disk_device, type_open)) >= 0) { + if (get_boot(try_only) < 0) { + list_disk_geometry(); + if (btrydev(device) < 0) + fprintf(stderr, + "Disk %s doesn't contain a valid " + "partition table\n", device); + close(fd); + } else { + close(fd); + list_table(0); + if (!sun_label && partitions > 4) + delete_partition(ext_index); + } + } else { + /* Ignore other errors, since we try IDE + and SCSI hard disks which may not be + installed on the system. */ + if(errno == EACCES) { + fprintf(stderr, "Cannot open %s\n", device); + return; + } + } + } +} + +void +dummy(int *kk) {} + +int +main(int argc, char **argv) +{ + int i, j, s, c; + int optl = 0, opts = 0; + char *part; + + + /* + * Calls: + * fdisk -v + * fdisk -l [-b sectorsize] [-u] [device] ... + * fdisk -s [partition] ... + * fdisk [-b sectorsize] [-u] [device] + */ + while ((c = getopt(argc, argv, "b:lsuv")) != EOF) { + switch (c) { + case 'b': + sector_size = atoi(optarg); + if (sector_size != 512 && sector_size != 1024 && + sector_size != 2048) + fatal(usage); + sector_offset = 2; + break; + case 'l': + optl = 1; + break; + case 's': + opts = 1; + break; + case 'u': + unit_flag = 0; + break; + case 'v': + printf("fdisk v" UTIL_LINUX_VERSION "\n"); + exit(0); + default: + fatal(usage); + } + } + + if (optl) { + listing = 1; + nowarn = 1; + type_open = O_RDONLY; + if (argc > optind) { + int k; + /* avoid gcc warning: + variable `k' might be clobbered by `longjmp' */ + dummy(&k); + for(k=optind; k<argc; k++) + try(argv[k], 1); + } else { + try("/dev/hda", 0); + try("/dev/hdb", 0); + try("/dev/hdc", 0); /* often a CDROM */ + try("/dev/hdd", 0); + try("/dev/sda", 0); + try("/dev/sdb", 0); + try("/dev/sdc", 0); + try("/dev/sdd", 0); + try("/dev/sde", 0); + try("/dev/sdf", 0); + try("/dev/sdg", 0); + try("/dev/sdh", 0); + try("/dev/eda", 0); /* PS/2 ESDI drives */ + try("/dev/edb", 0); + try("/dev/edc", 0); + try("/dev/edd", 0); + try("/dev/rd/c0d0", 0); /* DAC960 RAID disks */ + try("/dev/rd/c0d1", 0); + try("/dev/rd/c0d2", 0); + try("/dev/rd/c0d3", 0); + try("/dev/rd/c0d4", 0); + try("/dev/rd/c0d5", 0); + try("/dev/rd/c0d6", 0); + try("/dev/rd/c0d7", 0); + } + exit(0); + } + + if (opts) { + /* Very silly assumptions here about device naming */ + /* All this junk will disappear again */ + nowarn = 1; + disk_device = (char *) malloc(16); + type_open = O_RDONLY; + + opts = argc - optind; + if (opts <= 0) + fatal(usage); + + for (j = optind; j < argc; j++) { + part = argv[j]; + if (strncmp(part, "/dev/rd/", 8) == 0) { + char *p = strrchr(part, 'p'); + if (p == NULL) + fatal(usage); + if (!(i = atoi(p + 1))) + fatal(usage); + i--; /* count from 0 */ + *p = '\0'; + strcpy(disk_device, part); + } else { + if (strlen(part) < 9) + fatal(usage); + if (!(i = atoi(part + 8))) + fatal(usage); + i--; /* count from 0 */ + strncpy(disk_device, part, 8); + disk_device[8] = 0; + } + if ((fd = open(disk_device, type_open)) < 0) + fatal(unable_to_open); + close(fd); + if (get_boot(require) < 0) + exit(1); + if (i >= partitions) + exit(1); +#if defined(sparc) + if (!sun_label) { + int id = sunlabel->infos[i].id; + + if (!(id > 1 && id != WHOLE_DISK)) + exit(1); + s = get_num_sectors(sunlabel->partitions[i]); + } else +#endif + s = get_nr_sects(part_table[i]); + if (opts == 1) + printf("%d\n", s/2); + else + printf("%s: %d\n", argv[j], s/2); + } + exit(0); + } + + if (argc-optind == 1) + disk_device = argv[optind]; + else if (argc-optind != 0) + fatal(usage); + else { + if ((fd = open(DEFAULT_DEVICE, O_RDWR)) < 0) + disk_device = ALTERNATE_DEVICE; + else close(fd); + printf("Using %s as default device!\n", disk_device); + } + get_boot(fdisk); + + while (1) { + putchar('\n'); + switch (tolower(read_char("Command (m for help): "))) { + case 'a': + if (sun_label) + toggle_sunflags(get_partition(1, partitions), + 0x01); + else + if (sgi_label) + sgi_set_bootpartition( + get_partition(1, partitions)); + else + toggle_active(get_partition(1, partitions)); + break; + case 'b': + if (sgi_label) { + printf("\nThe current boot file is: %s\n", + sgi_get_bootfile()); + if (read_chars("Please enter the name of the " + "new boot file: ") == '\n') + printf("Boot file unchanged\n"); + else + sgi_set_bootfile(line_ptr); + } else + bselect(); + break; + case 'c': + if (sun_label) + toggle_sunflags(get_partition(1, partitions), + 0x10); + else + if (sgi_label) + sgi_set_swappartition( + get_partition(1, partitions)); + else + toggle_dos(); + break; + case 'd': + delete_partition( + get_partition(1, partitions)); + break; + case 'i': + if (sgi_label) + create_sgiinfo(); + else + menu(); + case 'l': + list_types(get_sys_types()); + break; + case 'n': + new_partition(); + break; + case 'o': + create_doslabel(); + break; + case 'p': + list_table(0); + break; + case 'q': + close(fd); + exit(0); + case 's': + create_sunlabel(); + break; + case 't': + change_sysid(); + break; + case 'u': + change_units(); + break; + case 'v': + verify(); + break; + case 'w': + write_table(); /* does not return */ + break; + case 'x': + if( sgi_label ) { + fprintf(stderr, + "\n\tSorry, no experts menu for SGI " + "partition tables available.\n\n"); + } else + xselect(); + break; + default: menu(); + } + } + return 0; +} diff --git a/fdisk/fdisk.h b/fdisk/fdisk.h new file mode 100644 index 000000000..080515e5f --- /dev/null +++ b/fdisk/fdisk.h @@ -0,0 +1,85 @@ +/* + fdisk.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 IS_EXTENDED(i) \ + ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED) + +#define SIZE(a) (sizeof(a)/sizeof((a)[0])) + +#define cround(n) (((n) + display_factor * unit_flag) / display_factor) + +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +extern ext2_loff_t ext2_llseek(unsigned int fd, + ext2_loff_t offset, + unsigned int origin); + +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 */ +}; + +enum failure {usage, unable_to_open, unable_to_read, unable_to_seek, + unable_to_write, out_of_memory, no_partition, no_device}; + +enum action {fdisk, require, try_only, create_empty}; + +struct systypes { + unsigned char index; + char *name; +}; + +/* prototypes for fdisk.c */ +extern char *disk_device, + *line_ptr; +extern int fd, + partitions; +extern uint unit_flag, + display_factor; +extern struct partition *part_table[]; +extern void fatal(enum failure why); +extern int get_boot(enum action what); +extern int get_partition(int warn, int max); +extern void list_types(struct systypes *sys); +extern int read_line (void); +extern char read_char(char *mesg); +extern int read_hex(struct systypes *sys); +uint read_int(uint low, uint dflt, uint high, uint base, char *mesg); +extern char *const str_units(void); + +extern unsigned int get_start_sect(struct partition *p); +extern unsigned int get_nr_sects(struct partition *p); + +/* prototypes for fdiskbsdlabel.c */ +extern void bselect(void); +extern int btrydev (char * dev); + +/* prototypes for fdisksgilabel.c */ +extern int valid_part_table_flag(unsigned char *b); diff --git a/fdisk/fdiskaixlabel.c b/fdisk/fdiskaixlabel.c new file mode 100644 index 000000000..848a6ece8 --- /dev/null +++ b/fdisk/fdiskaixlabel.c @@ -0,0 +1,64 @@ +#include <stdio.h> /* stderr */ +#include <stdlib.h> /* uint */ +#include <string.h> /* strstr */ +#include <unistd.h> /* write */ + +#include <endian.h> + +#include "fdisk.h" +#include "fdiskaixlabel.h" + +static int other_endian = 0; +static short volumes=1; + +/* + * only dealing with free blocks here + */ + +void +aix_info( void ) +{ + printf( + "\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).\n" + ); +} + +void +aix_nolabel( void ) +{ + aixlabel->magic = 0; + aix_label = 0; + partitions = 4; + memset( buffer, 0, sizeof(buffer) ); /* avoid fdisk cores */ + return; +} + +int +check_aix_label( void ) +{ + if (aixlabel->magic != AIX_LABEL_MAGIC && + aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) { + aix_label = 0; + other_endian = 0; + return 0; + } + other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED); + update_units(); + aix_label = 1; + partitions= 1016; + volumes = 15; + aix_info(); + aix_nolabel(); /* %% */ + aix_label = 1; /* %% */ + return 1; +} diff --git a/fdisk/fdiskaixlabel.h b/fdisk/fdiskaixlabel.h new file mode 100644 index 000000000..fd95bc015 --- /dev/null +++ b/fdisk/fdiskaixlabel.h @@ -0,0 +1,34 @@ +#include <linux/types.h> /* for __u32 etc */ +/* + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be redistributed under + * the terms of the GNU Public License. + */ + +typedef struct { + unsigned int magic; /* expect AIX_LABEL_MAGIC */ + unsigned int fillbytes1[124]; + unsigned int physical_volume_id; + unsigned int fillbytes2[124]; +} aix_partition; + +#define AIX_LABEL_MAGIC 0xc9c2d4c1 +#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9 +#define AIX_INFO_MAGIC 0x00072959 +#define AIX_INFO_MAGIC_SWAPPED 0x59290700 + +/* fdisk.c */ +#define aixlabel ((aix_partition *)buffer) +extern char buffer[MAX_SECTOR_SIZE]; +extern char changed[MAXIMUM_PARTS]; +extern uint heads, sectors, cylinders; +extern int show_begin; +extern int aix_label; +extern char *partition_type(unsigned char type); +extern void update_units(void); +extern char read_chars(char *mesg); + +/* fdiskaixlabel.c */ +extern struct systypes aix_sys_types[]; +extern void aix_nolabel( void ); +extern int check_aix_label( void ); diff --git a/fdisk/fdiskbsdlabel.c b/fdisk/fdiskbsdlabel.c new file mode 100644 index 000000000..8edec2331 --- /dev/null +++ b/fdisk/fdiskbsdlabel.c @@ -0,0 +1,825 @@ +/* + 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. +*/ + +#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 <sys/ioctl.h> +#include <sys/param.h> + +#include <linux/hdreg.h> /* for HDIO_GETGEO */ + +#include "fdisk.h" +#define NETBSD_PARTITION 0xa5 +#define DKTYPENAMES +#include "fdiskbsdlabel.h" + +static void xbsd_delete_part (void); +static void xbsd_new_part (void); +static void xbsd_print_disklabel (int show_all); +static void xbsd_write_disklabel (void); +static int xbsd_create_disklabel (void); +static void xbsd_edit_disklabel (void); +static void xbsd_write_bootstrap (void); +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 u_short xbsd_dkcksum (struct xbsd_disklabel *lp); +static int xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d, int pindex); +static int xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d); +static int xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d); +static void sync_disks (void); +#if defined (i386) || defined (sparc) +static int xbsd_translate_fstype (int linux_type); +static void xbsd_link_part (void); +#endif +#if defined (__alpha__) +void alpha_bootblock_checksum (char *boot); +#endif + +static struct xbsd_disklabel xbsd_dlabel; +static char buffer[BSD_BBSIZE]; +#if defined (i386) || defined (sparc) +static struct partition *xbsd_part; +static int xbsd_part_index; +#endif + +int +btrydev (char * dev) { + if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) + return -1; + printf("\nBSD label for device: %s\n", dev); + xbsd_print_disklabel (0); + return 0; +} + +void +bmenu (void) +{ + puts ("Command action\n" + " d delete a BSD partition\n" + " e edit drive data\n" + " i install bootstrap\n" + " l list known filesystem types\n" + " m print this menu\n" + " n add a new BSD partition\n" + " p print BSD partition table\n" + " q quit without saving changes\n" +#if defined (i386) || defined (sparc) + " r return to main menu\n" +#endif + " s show complete disklabel\n" + " t change a partition's filesystem id\n" + " w write disklabel to disk\n" +#if defined (i386) || defined (sparc) + " x link BSD partition to non-BSD partition" +#endif + ); +} + +int +hidden(int type) { + return type ^ 0x10; +} + +int +is_netbsd_partition_type(int type) { + return (type == NETBSD_PARTITION || type == hidden(NETBSD_PARTITION)); +} + +void +bselect (void) { +#if defined (i386) || defined (sparc) + int t, ss; + + for (t=0; t<4; t++) + if (is_netbsd_partition_type(part_table[t] -> sys_ind)) { + xbsd_part = part_table[t]; + xbsd_part_index = t; + ss = get_start_sect(xbsd_part); + if (ss == 0) { + fprintf (stderr, "Partition %s%d has invalid starting sector 0.\n", + disk_device, t+1); + return; + } + printf ("Reading disklabel of %s%d at sector %d.\n", + disk_device, t+1, ss + BSD_LABELSECTOR); + if (xbsd_readlabel (xbsd_part, &xbsd_dlabel) == 0) + if (xbsd_create_disklabel () == 0) + return; + break; + } + + if (t == 4) { + printf ("There is no *BSD partition on %s.\n", disk_device); + return; + } + +#elif defined (__alpha__) + + if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0) + if (xbsd_create_disklabel () == 0) + exit ( EXIT_SUCCESS ); + +#endif + + while (1) + { + putchar ('\n'); + switch (tolower (read_char ("BSD disklabel command (m for help): "))) + { + case 'd': + xbsd_delete_part (); + break; + case 'e': + xbsd_edit_disklabel (); + break; + case 'i': + xbsd_write_bootstrap (); + break; + case 'l': + xbsd_list_types (); + break; + case 'n': + xbsd_new_part (); + break; + case 'p': + xbsd_print_disklabel (0); + break; + case 'q': + close (fd); + exit ( EXIT_SUCCESS ); + case 's': + xbsd_print_disklabel (1); + break; + case 't': + xbsd_change_fstype (); + break; + case 'w': + xbsd_write_disklabel (); + break; +#if defined (i386) || defined (sparc) + case 'r': + return; + case 'x': + xbsd_link_part (); + break; +#endif + default: + bmenu (); + break; + } + } +} + +static void +xbsd_delete_part (void) +{ + int i; + + i = xbsd_get_part_index (xbsd_dlabel.d_npartitions); + xbsd_dlabel.d_partitions[i].p_size = 0; + xbsd_dlabel.d_partitions[i].p_offset = 0; + xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED; + if (xbsd_dlabel.d_npartitions == i + 1) + while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0) + xbsd_dlabel.d_npartitions--; +} + +static void +xbsd_new_part (void) +{ + uint begin, end; + char mesg[48]; + int i; + + if (!xbsd_check_new_partition (&i)) + return; + +#if defined (i386) || defined (sparc) + begin = get_start_sect(xbsd_part); + end = begin + get_nr_sects(xbsd_part) - 1; +#elif defined (__alpha__) || defined (__powerpc__) + begin = 0; + end = xbsd_dlabel.d_secperunit; +#endif + + sprintf (mesg, "First %s", str_units()); + begin = read_int (cround (begin), cround (begin), cround (end), + 0, mesg); + + sprintf (mesg, "Last %s or +size or +sizeM or +sizeK", str_units()); + end = read_int (cround (begin), cround (end), cround (end), + cround (begin), mesg); + + if (unit_flag) + { + begin = (begin - 1) * display_factor; + end = end * display_factor - 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 void +xbsd_print_disklabel (int show_all) +{ + struct xbsd_disklabel *lp = &xbsd_dlabel; + struct xbsd_partition *pp; + FILE *f = stdout; + int i, j; + + if (show_all) + { +#if defined (i386) || defined (sparc) + fprintf(f, "# %s%d:\n", disk_device, xbsd_part_index+1); +#elif defined (__alpha__) + fprintf(f, "# %s:\n", disk_device); +#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, "# size offset fstype [fsize bsize cpg]\n"); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + fprintf(f, " %c: %8ld %8ld ", 'a' + i, + (long) pp->p_size, (long) pp->p_offset); + 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, "%20.20s", ""); + break; + } + fprintf(f, "\t# (Cyl. %4ld", (long) +#if 0 + pp->p_offset / lp->d_secpercyl); /* differs from Linux fdisk */ +#else + pp->p_offset / lp->d_secpercyl + 1); +#endif + if (pp->p_offset % lp->d_secpercyl) + putc('*', f); + else + putc(' ', f); + fprintf(f, "- %ld", + (long) (pp->p_offset + + pp->p_size + lp->d_secpercyl - 1) / +#if 0 + lp->d_secpercyl - 1); /* differs from Linux fdisk */ +#else + lp->d_secpercyl); +#endif + if (pp->p_size % lp->d_secpercyl) + putc('*', f); + fprintf(f, ")\n"); + } + } +} + +static void +xbsd_write_disklabel (void) +{ +#if defined (i386) || defined (sparc) + printf ("Writing disklabel to %s%d.\n", disk_device, xbsd_part_index+1); + xbsd_writelabel (xbsd_part, &xbsd_dlabel); +#elif defined (__alpha__) + printf ("Writing disklabel to %s.\n", disk_device); + xbsd_writelabel (NULL, &xbsd_dlabel); +#endif +} + +static int +xbsd_create_disklabel (void) +{ + char c; + +#if defined (i386) || defined (sparc) + fprintf (stderr, "%s%d contains no disklabel.\n", + disk_device, xbsd_part_index+1); +#elif defined (__alpha__) + fprintf (stderr, "%s contains no disklabel.\n", disk_device); +#endif + + while (1) + if ((c = tolower (read_char ("Do you want to create a disklabel? (y/n) "))) == 'y') + { +#if defined (i386) || defined (sparc) + if (xbsd_initlabel (xbsd_part, &xbsd_dlabel, xbsd_part_index) == 1) +#elif defined (__alpha__) || defined (__powerpc__) + if (xbsd_initlabel (NULL, &xbsd_dlabel, 0) == 1) +#endif + { + xbsd_print_disklabel (1); + return 1; + } + else + return 0; + } + else if (c == 'n') + return 0; +} + +static int +edit_int (int def, char *mesg) +{ + do { + fputs (mesg, stdout); + printf (" (%d): ", def); + if (!read_line ()) + return def; + } + while (!isdigit (*line_ptr)); + return atoi (line_ptr); +} + +static void +xbsd_edit_disklabel (void) +{ + struct xbsd_disklabel *d; + + d = &xbsd_dlabel; + +#ifdef __alpha__ + d -> d_secsize = (u_long) edit_int ((u_long) d -> d_secsize ,"bytes/sector"); + d -> d_nsectors = (u_long) edit_int ((u_long) d -> d_nsectors ,"sectors/track"); + d -> d_ntracks = (u_long) edit_int ((u_long) d -> d_ntracks ,"tracks/cylinder"); + d -> d_ncylinders = (u_long) edit_int ((u_long) d -> d_ncylinders ,"cylinders"); +#endif + + /* d -> d_secpercyl can be != d -> d_nsectors * d -> d_ntracks */ + while (1) + { + d -> d_secpercyl = (u_long) edit_int ((u_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 = (u_short) edit_int ((u_short) d -> d_rpm ,"rpm"); + d -> d_interleave = (u_short) edit_int ((u_short) d -> d_interleave,"interleave"); + d -> d_trackskew = (u_short) edit_int ((u_short) d -> d_trackskew ,"trackskew"); + d -> d_cylskew = (u_short) edit_int ((u_short) d -> d_cylskew ,"cylinderskew"); + d -> d_headswitch = (u_long) edit_int ((u_long) d -> d_headswitch ,"headswitch"); + d -> d_trkseek = (u_long) edit_int ((u_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 (void) +{ + char *bootdir = BSD_LINUX_BOOTDIR; + char path[MAXPATHLEN]; + 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 ()) + { + line_ptr[strlen (line_ptr)-1] = '\0'; + dkbasename = line_ptr; + } + sprintf (path, "%s/%sboot", bootdir, dkbasename); + if (!xbsd_get_bootstrap (path, buffer, (int) xbsd_dlabel.d_secsize)) + return; + + /* We need a backup of the disklabel (xbsd_dlabel might have changed). */ + d = &buffer[BSD_LABELSECTOR * SECTOR_SIZE]; + bcopy (d, &dl, sizeof (struct xbsd_disklabel)); + + /* The disklabel will be overwritten by 0's from bootxx anyway */ + bzero (d, sizeof (struct xbsd_disklabel)); + + sprintf (path, "%s/boot%s", bootdir, dkbasename); + if (!xbsd_get_bootstrap (path, &buffer[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 ); + } + + bcopy (&dl, d, sizeof (struct xbsd_disklabel)); + +#if defined (i386) || defined (sparc) + sector = get_start_sect(xbsd_part); +#elif defined (__powerpc__) + sector = 0; +#elif defined (__alpha__) + sector = 0; + alpha_bootblock_checksum (buffer); +#endif + + if (ext2_llseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1) + fatal (unable_to_seek); + if (BSD_BBSIZE != write (fd, buffer, BSD_BBSIZE)) + fatal (unable_to_write); + +#if defined (i386) || defined (sparc) + printf ("Bootstrap installed on %s%d.\n", disk_device, xbsd_part_index+1); +#elif defined (__alpha__) + printf ("Bootstrap installed on %s.\n", disk_device); +#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[40]; + char l; + + sprintf (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) +{ + int t; + + if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) + { + 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 u_short +xbsd_dkcksum (struct xbsd_disklabel *lp) +{ + register u_short *start, *end; + register u_short sum = 0; + + start = (u_short *)lp; + end = (u_short *)&lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} + +static int +xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d, int pindex) +{ + struct hd_geometry geometry; + struct xbsd_partition *pp; + + if (ioctl (fd, HDIO_GETGEO, &geometry) == -1) + { + perror ("ioctl"); + return 0; + } + bzero (d, sizeof (struct xbsd_disklabel)); + + d -> d_magic = BSD_DISKMAGIC; + + if (strncmp (disk_device, "/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 (i386) || defined (sparc) + d -> d_flags = BSD_D_DOSPART; +#else + d -> d_flags = 0; +#endif + d -> d_secsize = SECTOR_SIZE; /* bytes/sector */ + d -> d_nsectors = geometry.sectors; /* sectors/track */ + d -> d_ntracks = geometry.heads; /* tracks/cylinder (heads) */ + d -> d_ncylinders = geometry.cylinders; + d -> d_secpercyl = geometry.sectors * geometry.heads; /* sectors/cylinder */ + 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 (i386) || defined (sparc) + 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; +} + +static int +xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d) +{ + int t, sector; + +#if defined (i386) || defined (sparc) + sector = (p ? get_start_sect(p) : 0); +#elif defined (__alpha__) + sector = 0; +#endif + + if (ext2_llseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1) + fatal (unable_to_seek); + if (BSD_BBSIZE != read (fd, buffer, BSD_BBSIZE)) + fatal (unable_to_read); + + bcopy (&buffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], + d, sizeof (struct xbsd_disklabel)); + + 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_magic != BSD_DISKMAGIC || d -> d_magic2 != BSD_DISKMAGIC) + return 0; + + 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 partition *p, struct xbsd_disklabel *d) +{ + int sector; + +#if defined (i386) || defined (sparc) + sector = get_start_sect(p) + BSD_LABELSECTOR; +#elif defined (__alpha__) || defined (__powerpc__) + 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. + */ + bcopy (d, &buffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], + sizeof (struct xbsd_disklabel)); + +#if defined (__alpha__) && BSD_LABELSECTOR == 0 + alpha_bootblock_checksum (buffer); + if (ext2_llseek (fd, 0, SEEK_SET) == -1) + fatal (unable_to_seek); + if (BSD_BBSIZE != write (fd, buffer, BSD_BBSIZE)) + fatal (unable_to_write); +#else + if (ext2_llseek (fd, sector * SECTOR_SIZE + BSD_LABELOFFSET, SEEK_SET) == -1) + fatal (unable_to_seek); + if (sizeof (struct xbsd_disklabel) != write (fd, d, sizeof (struct xbsd_disklabel))) + fatal (unable_to_write); +#endif + + sync_disks (); + + return 1; +} + +static void +sync_disks (void) +{ + printf ("\nSyncing disks.\n"); + sync (); + sleep (4); +} + +#if defined (i386) || defined (sparc) +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 (void) +{ + int k, i; + + k = get_partition (1, partitions); + + if (!xbsd_check_new_partition (&i)) + return; + + xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(part_table[k]); + xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(part_table[k]); + xbsd_dlabel.d_partitions[i].p_fstype = + xbsd_translate_fstype (part_table[k] -> sys_ind); +} +#endif + +#if defined (__alpha__) + +#if 0 +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__ */ diff --git a/fdisk/fdiskbsdlabel.h b/fdisk/fdiskbsdlabel.h new file mode 100644 index 000000000..b9f2c9c38 --- /dev/null +++ b/fdisk/fdiskbsdlabel.h @@ -0,0 +1,229 @@ +/* + * 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 <linux/types.h> /* for __u32 etc */ + +#ifndef BSD_DISKMAGIC /* perhaps from <linux/genhd.h> */ +#define BSD_DISKMAGIC ((__u32) 0x82564557) +#endif + +#ifndef BSD_MAXPARTITIONS +#define BSD_MAXPARTITIONS 8 +#endif + +#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec" + +#if defined (i386) || defined (sparc) +#define BSD_LABELSECTOR 1 +#define BSD_LABELOFFSET 0 +#elif defined (__alpha__) || defined (__powerpc__) +#define BSD_LABELSECTOR 0 +#define BSD_LABELOFFSET 64 +#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 { + __u32 d_magic; /* the magic number */ + __s16 d_type; /* drive type */ + __s16 d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + char d_packname[16]; /* pack identifier */ + /* disk geometry: */ + __u32 d_secsize; /* # of bytes per sector */ + __u32 d_nsectors; /* # of data sectors per track */ + __u32 d_ntracks; /* # of tracks per cylinder */ + __u32 d_ncylinders; /* # of data cylinders per unit */ + __u32 d_secpercyl; /* # of data sectors per cylinder */ + __u32 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. + */ + __u16 d_sparespertrack; /* # of spare sectors per track */ + __u16 d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + __u32 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. + */ + __u16 d_rpm; /* rotational speed */ + __u16 d_interleave; /* hardware sector interleave */ + __u16 d_trackskew; /* sector 0 skew, per track */ + __u16 d_cylskew; /* sector 0 skew, per cylinder */ + __u32 d_headswitch; /* head switch time, usec */ + __u32 d_trkseek; /* track-to-track seek, usec */ + __u32 d_flags; /* generic flags */ +#define NDDATA 5 + __u32 d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + __u32 d_spare[NSPARE]; /* reserved for future use */ + __u32 d_magic2; /* the magic number (again) */ + __u16 d_checksum; /* xor of data incl. partitions */ + /* filesystem and partition information: */ + __u16 d_npartitions; /* number of partitions in following */ + __u32 d_bbsize; /* size of boot area at sn0, bytes */ + __u32 d_sbsize; /* max size of fs superblock, bytes */ + struct xbsd_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 */ +}; + +/* 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 */ + +/* this is annoying, but it's also the way it is :-( */ +#ifdef __alpha__ +#define BSD_FS_EXT2 8 /* MS-DOS 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"}, + { 0, NULL } +}; +#define BSD_FSMAXTYPES (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 */ diff --git a/fdisk/fdisksgilabel.c b/fdisk/fdisksgilabel.c new file mode 100644 index 000000000..42f7363e4 --- /dev/null +++ b/fdisk/fdisksgilabel.c @@ -0,0 +1,888 @@ +/* + * + * fdisksgilabel.c + * + * Copyright (C) Andreas Neuper, Sep 1998. + * This file may be modified and redistributed under + * the terms of the GNU Public License. + */ +#include <stdio.h> /* stderr */ +#include <stdlib.h> /* uint */ +#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 <linux/major.h> /* FLOPPY_MAJOR */ +#include <linux/hdreg.h> /* HDIO_GETGEO */ + +#include "fdisk.h" +#include "fdisksgilabel.h" + +static int other_endian = 0; +static int debug = 0; +static short volumes=1; + +/* + * only dealing with free blocks here + */ + +typedef struct { int first; int last; } freeblocks; +static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */ +void setfreelist( int i, int f, int l ) \ + { freelist[i].first = f; freelist[i].last = l; return; } +void add2freelist( int f, int l ) \ + { int i = 0; for( ; i<17 ; i++ ) { if(freelist[i].last==0) break; }\ + setfreelist( i, f, l ); return; } +void clearfreelist( void ) \ + { int i = 0; for( ; i<17 ; i++ ) { setfreelist( i, 0, 0 ); } return; } +int isinfreelist( int b ) \ + { int i = 0; for( ; 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, "SGI volhdr"}, + {0x01, "SGI trkrepl"}, + {0x02, "SGI secrepl"}, + {SGI_SWAP, "SGI raw"}, + {0x04, "SGI bsd"}, + {0x05, "SGI sysv"}, + {ENTIRE_DISK, "SGI volume"}, + {SGI_EFS, "SGI efs"}, + {0x08, "SGI lvol"}, + {0x09, "SGI rlvol"}, + {0x0A, "SGI xfs"}, + {0x0B, "SGI xlvol"}, + {0x0C, "SGI rxlvol"}, + {LINUX_SWAP, "Linux swap"}, + {LINUX_NATIVE,"Linux native"}, + {0, NULL } +}; + +static inline unsigned short __swap16(unsigned short x) { + return (((__u16)(x) & 0xFF) << 8) | (((__u16)(x) & 0xFF00) >> 8); +} +static inline __u32 __swap32(__u32 x) { + return (((__u32)(x) & 0xFF) << 24) | (((__u32)(x) & 0xFF00) << 8) | (((__u32)(x) & 0xFF0000) >> 8) | (((__u32)(x) & 0xFF000000) >> 24); +} + +static +int +sgi_get_nsect( void ) +{ + return SSWAP16(sgilabel->devparam.nsect); +} + +static +int +sgi_get_ntrks( void ) +{ + return SSWAP16(sgilabel->devparam.ntrks); +} + +#if 0 +static int +sgi_get_head_vol0( void ) +{ + return SSWAP16(sgilabel->devparam.head_vol0); +} + +static int +sgi_get_bytes( void ) +{ + return SSWAP16(sgilabel->devparam.bytes); +} +#endif + +static +int +sgi_get_pcylcount( void ) +{ + return SSWAP16(sgilabel->devparam.pcylcount); +} + +void +sgi_nolabel() +{ + sgilabel->magic = 0; + sgi_label = 0; + partitions = 4; +} + +unsigned int +two_s_complement_32bit_sum( + unsigned int* base, + int size /* in bytes */ ) +{ + int i=0; + unsigned int sum=0; + size = size / sizeof( unsigned int ); + for( i=0; i<size; i++ ) + { + sum = sum - SSWAP32(base[i]); + } + return sum; +} + +int check_sgi_label() +{ + 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) { + sgi_label = 0; + 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" ); + } else + { + heads = sgi_get_ntrks(); + cylinders = sgi_get_pcylcount(); + sectors = sgi_get_nsect(); + } + update_units(); + sgi_label = 1; + partitions= 16; + volumes = 15; + return 1; +} + +void +sgi_list_table( int xtra ) +{ + int i, w; + char *type; + + w = strlen( disk_device ); + + if( xtra ) + { + printf( "\nDisk %s (SGI disk label): %d heads, %d sectors\n" + "%d cylinders, %d physical cylinders\n" + "%d extra sects/cyl, interleave %d:1\n" + "%s\n" + "Units = %ss of %d * 512 bytes\n\n", + disk_device, heads, sectors, cylinders, + SSWAP16(sgiparam.pcylcount), + SSWAP16(sgiparam.sparecyl), + SSWAP16(sgiparam.ilfact), + (char *)sgilabel, + str_units(), display_factor); + } else + { + printf( "\nDisk %s (SGI disk label): %d heads, %d sectors, %d cylinders\n" + "Units = %ss of %d * 512 bytes\n\n", + disk_device, heads, sectors, cylinders, + str_units(), display_factor ); + } + printf("----- partitions -----\n" + "%*s Info Start End Sectors Id System\n", + w + 1, "Device"); + for (i = 0 ; i < partitions; i++) + { + if( sgi_get_num_sectors(i) || debug ) + { + __u32 start = sgi_get_start_sector(i); + __u32 len = sgi_get_num_sectors(i); + printf( + "%*s%-2d %4s %9ld %9ld %9ld %2x %s\n", +/* device */ w, disk_device, i + 1, +/* flags */ (sgi_get_swappartition() == i) ? "swap" : +/* flags */ (sgi_get_bootpartition() == i) ? "boot" : " ", +/* start */ (long) scround(start), +/* end */ (long) scround(start+len)-1, +/* no odd flag on end */ (long) len, +/* type id */ sgi_get_sysid(i), +/* type name */ (type = partition_type(sgi_get_sysid(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) + { + __u32 start = SSWAP32(sgilabel->directory[i].vol_file_start); + __u32 len = SSWAP32(sgilabel->directory[i].vol_file_size); + char*name = sgilabel->directory[i].vol_file_name; + printf("%2d: %-10s sector%5u size%8u\n", + i, name, (unsigned int) start, (unsigned int) len); + } + } +} + +int +sgi_get_start_sector( int i ) +{ + return SSWAP32(sgilabel->partitions[i].start_sector); +} + +int +sgi_get_num_sectors( int i ) +{ + return SSWAP32(sgilabel->partitions[i].num_sectors); +} + +int +sgi_get_sysid( int i ) +{ + return SSWAP32(sgilabel->partitions[i].id); +} + +int +sgi_get_bootpartition( void ) +{ + return SSWAP16(sgilabel->boot_part); +} + +int +sgi_get_swappartition( void ) +{ + return SSWAP16(sgilabel->swap_part); +} + +void +sgi_set_bootpartition( int i ) +{ + sgilabel->boot_part = SSWAP16(((short)i)); + return; +} + +int +sgi_get_lastblock( void ) +{ + return heads * sectors * cylinders; +} + +void +sgi_set_swappartition( int i ) +{ + sgilabel->swap_part = SSWAP16(((short)i)); + return; +} + +static int +sgi_check_bootfile( 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, 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 */ +} + +const char * +sgi_get_bootfile(void) { + return sgilabel->boot_file; +} + +void +sgi_set_bootfile( const char* aFile ) +{ + int i = 0; + if( sgi_check_bootfile( aFile ) ) + { + while( i<16 ) + { + if( (aFile[i] != '\n') /* in principle caught again by next line */ + && (strlen( aFile ) > i ) ) + sgilabel->boot_file[i] = aFile[i]; + else + sgilabel->boot_file[i] = 0; + i++; + } + printf( "\n\tBootfile is changed to \"%s\".\n", sgilabel->boot_file ); + } + return; +} + +void +create_sgiinfo( void ) +{ + /* 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( sgilabel->directory[0].vol_file_name, "sgilabel",8 ); + return; +} + +sgiinfo * fill_sgiinfo( void ); + +void +sgi_write_table( void ) +{ + 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(fd, 0, SEEK_SET) < 0 ) + fatal(unable_to_seek); + if( write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE ) + fatal(unable_to_write); + if( ! strncmp( sgilabel->directory[0].vol_file_name, "sgilabel",8 ) ) + { + /* + * keep this habbit of first writing the "sgilabel". + * I never tested whether it works without (AN 981002). + */ + sgiinfo*info = fill_sgiinfo(); /* fills the block appropriately */ + int infostartblock = SSWAP32( sgilabel->directory[0].vol_file_start ); + if( ext2_llseek(fd, (ext2_loff_t)infostartblock* + SECTOR_SIZE, SEEK_SET) < 0 ) + fatal(unable_to_seek); + if( write(fd, info, SECTOR_SIZE) != SECTOR_SIZE ) + fatal(unable_to_write); + free( info ); + } + return; +} + +static +int +compare_start( int*x, int*y ) +{ + /* + * sort according to start sectors + * and prefers largest partition: + * entry zero is entire disk entry + */ + int i = *x; + int j = *y; + int a = sgi_get_start_sector(i); + int b = sgi_get_start_sector(j); + int c = sgi_get_num_sectors(i); + int d = sgi_get_num_sectors(j); + if( a == b ) + { + return( d - c ); + } + return( a - b ); +} + +static +int +sgi_gaps() +{ + /* + * returned value is: + * = 0 : disk is properly filled to the rim + * < 0 : there is an overlap + * > 0 : there is still some vacant space + */ + return verify_sgi(0); +} + +int +verify_sgi( int verbose ) +{ + int Index[16]; /* list of valid partitions */ + int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */ + int compare_start();/* comparison function above */ + int entire = 0, i = 0; /* local counters */ + int start = 0; + int gap = 0; /* count unused blocks */ + int lastblock = sgi_get_lastblock(); + /* + */ + clearfreelist(); + for( i=0; i<16; i++ ) + { + if( sgi_get_num_sectors(i)!=0 ) + { + Index[sortcount++]=i; + if( sgi_get_sysid(i) == ENTIRE_DISK ) + { + if( entire++ == 1 ) + { + if(verbose) + printf("More than one entire disk entriy present.\n"); + } + } + } + } + if( sortcount == 0 ) + { + if(verbose) + printf("No partitions defined\n"); + return lastblock; + } + qsort( Index, sortcount, sizeof(Index[0]), (void*)compare_start ); + if( sgi_get_sysid( 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( Index[0] ) != 0 ) && verbose ) + printf( "The entire disk partition should start at block 0,\nnot " + "at diskblock %d.\n", sgi_get_start_sector(Index[0] ) ); + if(debug) /* I do not understand how some disks fulfil it */ + if( ( sgi_get_num_sectors( 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( Index[0] ), lastblock ); + lastblock = sgi_get_num_sectors( Index[0] ); + } else + { + if( verbose ) + printf( "One Partition (#11) should cover the entire disk.\n" ); + if(debug>2) + printf( "sysid=%d\tpartition=%d\n", + sgi_get_sysid( Index[0] ), Index[0]+1 ); + } + for( i=1, start=0; i<sortcount; i++ ) + { + int cylsize = sgi_get_nsect() * sgi_get_ntrks(); + if( (sgi_get_start_sector( 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( 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( Index[i] ) == ENTIRE_DISK ) continue; + if( start > sgi_get_start_sector( 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( Index[i] ) ); + if( gap > 0 ) gap = -gap; + if( gap == 0 ) gap = -1; + } + if( start < sgi_get_start_sector( Index[i] ) ) + { + if( verbose ) + printf( "Unused gap of %8d sectors - sectors %8d-%d\n", + sgi_get_start_sector( Index[i] ) - start, + start, sgi_get_start_sector( Index[i] )-1 ); + gap += sgi_get_start_sector( Index[i] ) - start; + add2freelist( start, sgi_get_start_sector( Index[i] ) ); + } + start = sgi_get_start_sector( Index[i] ) + + sgi_get_num_sectors( Index[i] ); + if(debug>1) + { + if( verbose ) + printf( "%2d:%12d\t%12d\t%12d\n", Index[i], + sgi_get_start_sector(Index[i]), + sgi_get_num_sectors(Index[i]), + sgi_get_sysid(Index[i]) ); + } + } + if( ( start < lastblock ) ) + { + if( verbose ) + printf( "Unused gap of %8d sectors - sectors %8d-%d\n", + lastblock - start, start, lastblock-1 ); + gap += lastblock - start; + add2freelist( start, lastblock ); + } + /* + * Done with arithmetics + * Go for details now + */ + if( verbose ) + { + if( !sgi_get_num_sectors( sgi_get_bootpartition() ) ) + { + printf( "\nThe boot partition does not exist.\n" ); + } + if( !sgi_get_num_sectors( sgi_get_swappartition() ) ) + { + printf( "\nThe swap partition does not exist.\n" ); + } else + if( ( sgi_get_sysid( sgi_get_swappartition() ) != SGI_SWAP ) + && ( sgi_get_sysid( sgi_get_swappartition() ) != LINUX_SWAP ) ) + { + printf( "\nThe swap partition has no swap type.\n" ); + } + if( sgi_check_bootfile( "/unix" ) ) + { + printf( "\tYou have chosen an unusual boot file name.\n" ); + } + } + return gap; +} + +void +sgi_change_sysid( int i, int sys ) +{ + if( sgi_get_num_sectors(i) == 0 ) /* caught already before, ... */ + { + printf("Sorry You may change the Tag of non-empty partitions.\n"); + return; + } + if( ((sys != ENTIRE_DISK ) && (sys != SGI_VOLHDR)) + && (sgi_get_start_sector(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; + } + sgilabel->partitions[i].id = SSWAP32(sys); + return; +} + +int +sgi_entire( void ) +{ /* returns partition index of first entry marked as entire disk */ + int i=0; + for( i=0; i<16; i++ ) + if( sgi_get_sysid(i) == SGI_VOLUME ) + return i; + return -1; +} + +int +sgi_num_partitions( void ) +{ + int i=0, + n=0; + for( i=0; i<16; i++ ) + if( sgi_get_num_sectors(i)!=0 ) + n++; + return n; +} + +static +void +sgi_set_partition( int i, uint start, uint length, int sys ) +{ + sgilabel->partitions[i].id = + SSWAP32( sys ); + sgilabel->partitions[i].num_sectors = + SSWAP32( length ); + sgilabel->partitions[i].start_sector = + SSWAP32( start ); + changed[i] = 1; + if( sgi_gaps(0) < 0 ) /* rebuild freelist */ + { + printf( "Do You know, You got a partition overlap on the disk?\n" ); + } + return; +} + +static +void +sgi_set_entire( void ) +{ + int n; + for( n=10; n<partitions; n++ ) + { + if(!sgi_get_num_sectors( n ) ) + { + sgi_set_partition( n, 0, sgi_get_lastblock(), SGI_VOLUME ); + break; + } + } + return; +} + +static +void +sgi_set_volhdr( void ) +{ + int n; + for( n=8; n<partitions; n++ ) + { + if(!sgi_get_num_sectors( n ) ) + { + /* + * 5 cylinders is an arbitrary value I like + * IRIX 5.3 stored files in the volume header + * (like sash, symmon, fx, ide) with ca. 3200 + * sectors. + */ + if( heads * sectors * 5 < sgi_get_lastblock() ) + sgi_set_partition( n, 0, heads * sectors * 5, SGI_VOLHDR ); + break; + } + } + return; +} + +void +sgi_delete_partition( int i ) +{ + sgi_set_partition( i, 0, 0, 0 ); + return; +} + +void +sgi_add_partition( int n, int sys ) +{ + char mesg[48]; + int first=0, last=0; + + if( n == 10 ) { + sys = SGI_VOLUME; + } else if ( n == 8 ) { + sys = 0; + } + if( sgi_get_num_sectors(n) ) + { + printf( "Partition %d is already defined. Delete " + "it before re-adding it.\n", n + 1); + return; + } + if( (sgi_entire() == -1) + && (sys != SGI_VOLUME) ) + { + printf( "Attempting to generate entire disk entry automatically.\n" ); + sgi_set_entire(); + sgi_set_volhdr(); + } + if( (sgi_gaps() == 0) + && (sys != SGI_VOLUME) ) + { + printf( "The entire disk is already covered with partitions.\n" ); + return; + } + if( sgi_gaps() < 0 ) + { + printf( "You got a partition overlap on the disk. Fix it first!\n" ); + return; + } + sprintf(mesg, "First %s", str_units()); + for(;;) + { + if(sys == SGI_VOLUME) + { + last = sgi_get_lastblock(); + first = read_int(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(scround(first), scround(first), scround(last)-1, + 0, mesg); + } + if (unit_flag) + first *= display_factor; + 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; + } + sprintf(mesg, " Last %s", str_units()); + last = read_int(scround(first), scround(last)-1, scround(last)-1, + scround(first), mesg)+1; + if (unit_flag) + last *= display_factor; + else + last = last; /* align to cylinder if You know how ... */ + if( (sys == SGI_VOLUME) && ( first != 0 || last != sgi_get_lastblock() ) ) + { + printf( "It is highly recommended that eleventh partition\n" + "covers the entire disk and is of type `SGI volume'\n"); + } + sgi_set_partition( n, first, last-first, sys ); + return; +} + +void +create_sgilabel( void ) +{ + struct hd_geometry geometry; + struct { int start; + int nsect; + int sysid; } old[4]; + int i=0; + fprintf( stderr, + "Building a new SGI disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content will be unrecoverable lost.\n\n"); +#if BYTE_ORDER == LITTLE_ENDIAN + other_endian = 1; +#else + other_endian = 0; +#endif +#ifdef HDIO_REQ + if (!ioctl(fd, HDIO_REQ, &geometry)) +#else + if (!ioctl(fd, HDIO_GETGEO, &geometry)) +#endif + { + heads = geometry.heads; + sectors = geometry.sectors; + cylinders = geometry.cylinders; + } + for (i = 0; i < 4; i++) + { + old[i].sysid = 0; + if( valid_part_table_flag(buffer) ) + { + if( part_table[i]->sys_ind ) + { + old[i].sysid = part_table[i]->sys_ind; + old[i].start = get_start_sect( part_table[i] ); + old[i].nsect = get_nr_sects( part_table[i] ); + printf( "Trying to keep parameters of partition %d.\n", i ); + if( debug ) + printf( "ID=%02x\tSTART=%d\tLENGTH=%d\n", + old[i].sysid, old[i].start, old[i].nsect ); + } + } + } + memset(buffer, 0, SECTOR_SIZE); + sgilabel->magic = SSWAP32(SGI_LABEL_MAGIC); + sgilabel->boot_part = SSWAP16(0); + sgilabel->swap_part = SSWAP16(1); strncpy( + sgilabel->boot_file , "/unix\0\0\0\0\0\0\0\0\0\0\0", 16 ); + 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(512); + 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 ); + sgi_label = 1; + partitions = 16; + volumes = 15; + sgi_set_entire(); + sgi_set_volhdr(); + for (i = 0; i < 4; i++) + { + if( old[i].sysid ) + { + sgi_set_partition( i, old[i].start, old[i].nsect, old[i].sysid ); + } + } + return; +} + +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 */ +} + +/* _____________________________________________________________ + */ + +sgiinfo* +fill_sgiinfo( void ) +{ + sgiinfo*info=calloc( 1, sizeof(sgiinfo) ); + 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( info->scsi_string, "IBM OEM 0662S12 3 30" ); + strcpy( info->serial, "0000" ); + info->check1816 = SSWAP16(18*256 +16 ); + strcpy( info->installer, "Sfx version 5.3, Oct 18, 1994" ); + return info; +} diff --git a/fdisk/fdisksgilabel.h b/fdisk/fdisksgilabel.h new file mode 100644 index 000000000..6eb7293f9 --- /dev/null +++ b/fdisk/fdisksgilabel.h @@ -0,0 +1,133 @@ +#include <linux/types.h> /* for __u32 etc */ +/* + * 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_VOLUME 0x06 +#define SGI_EFS 0x07 +#define SGI_SWAP 0x03 +#define SGI_VOLHDR 0x00 +#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 ENABLE_CMDTAGQ 0x40 + +typedef struct { + unsigned int magic; /* expect SGI_LABEL_MAGIC */ + unsigned short boot_part; /* active boot partition */ + unsigned 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]; /* an 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; /* 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 ? __swap16(x) \ + : (__u16)(x)) +#define SSWAP32(x) (other_endian ? __swap32(x) \ + : (__u32)(x)) +#define scround(x) ((x+(display_factor-1)*unit_flag)/display_factor) + +/* fdisk.c */ +#define sgilabel ((sgi_partition *)buffer) +#define sgiparam (sgilabel->devparam) +extern char buffer[MAX_SECTOR_SIZE]; +extern char changed[MAXIMUM_PARTS]; +extern uint heads, sectors, cylinders; +extern int show_begin; +extern int sgi_label; +extern char *partition_type(unsigned char type); +extern void update_units(void); +extern char read_chars(char *mesg); + +/* fdisksgilabel.c */ +extern struct systypes sgi_sys_types[]; +extern void sgi_nolabel( void ); +extern int check_sgi_label( void ); +extern void sgi_list_table( int xtra ); +extern void sgi_change_sysid( int i, int sys ); +extern int sgi_get_start_sector( int i ); +extern int sgi_get_num_sectors( int i ); +extern int sgi_get_sysid( int i ); +extern void sgi_delete_partition( int i ); +extern void sgi_add_partition( int n, int sys ); +extern void create_sgilabel( void ); +extern void create_sgiinfo( void ); +extern int verify_sgi( int verbose ); +extern void sgi_write_table( void ); +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( int i ); +extern void sgi_set_swappartition( int i ); +extern int sgi_get_bootpartition( void ); +extern int sgi_get_swappartition( void ); +extern void sgi_set_bootfile( const char* aFile ); +extern const char *sgi_get_bootfile( void ); diff --git a/fdisk/fdisksunlabel.c b/fdisk/fdisksunlabel.c new file mode 100644 index 000000000..708377379 --- /dev/null +++ b/fdisk/fdisksunlabel.c @@ -0,0 +1,690 @@ +/* + * 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. + */ + +#include <stdio.h> /* stderr */ +#include <stdlib.h> /* uint */ +#include <string.h> /* strstr */ +#include <unistd.h> /* write */ +#include <sys/ioctl.h> /* ioctl */ +#include <sys/stat.h> /* stat */ + +#include <endian.h> +#if 1 +#include <scsi/scsi.h> /* SCSI_IOCTL_GET_IDLUN */ +#endif +#include <linux/major.h> /* FLOPPY_MAJOR */ +#include <linux/hdreg.h> /* HDIO_GETGEO */ + +#include "fdisk.h" +#include "fdisksunlabel.h" + +static int other_endian = 0; +static int scsi_disk = 0; +static int floppy = 0; + +#define LINUX_SWAP 0x82 +#define LINUX_NATIVE 0x83 + +struct systypes sun_sys_types[] = { + {0, "Empty"}, + {1, "Boot"}, + {2, "SunOS root"}, + {SUNOS_SWAP, "SunOS swap"}, + {4, "SunOS usr"}, + {WHOLE_DISK, "Whole disk"}, + {6, "SunOS stand"}, + {7, "SunOS var"}, + {8, "SunOS home"}, + {LINUX_SWAP, "Linux swap"}, + {LINUX_NATIVE, "Linux native"}, + { 0, NULL } +}; + +static inline unsigned short __swap16(unsigned short x) { + return (((__u16)(x) & 0xFF) << 8) | (((__u16)(x) & 0xFF00) >> 8); +} +static inline __u32 __swap32(__u32 x) { + return (((__u32)(x) & 0xFF) << 24) | (((__u32)(x) & 0xFF00) << 8) | (((__u32)(x) & 0xFF0000) >> 8) | (((__u32)(x) & 0xFF000000) >> 24); +} + +int +get_num_sectors(struct sun_partition p) { + return SSWAP32(p.num_sectors); +} + +void guess_device_type(int fd) { + struct stat bootstat; + + if (fstat (fd, &bootstat) < 0) { + scsi_disk = 0; + floppy = 0; + } else if (S_ISBLK(bootstat.st_mode) + && ((bootstat.st_rdev >> 8) == IDE0_MAJOR || + (bootstat.st_rdev >> 8) == IDE1_MAJOR)) { + scsi_disk = 0; + floppy = 0; + } else if (S_ISBLK(bootstat.st_mode) + && (bootstat.st_rdev >> 8) == FLOPPY_MAJOR) { + scsi_disk = 0; + floppy = 1; + } else { + scsi_disk = 1; + floppy = 0; + } +} + +void set_sun_partition(int i, uint start, uint stop, int sysid) +{ + sunlabel->infos[i].id = sysid; + sunlabel->partitions[i].start_cylinder = + SSWAP32(start / (heads * sectors)); + sunlabel->partitions[i].num_sectors = + SSWAP32(stop - start); + changed[i] = 1; +} + +void sun_nolabel(void) +{ + sun_label = 0; + sunlabel->magic = 0; + partitions = 4; +} + +int check_sun_label(void) +{ + unsigned short *ush; + int csum; + + if (sunlabel->magic != SUN_LABEL_MAGIC && + sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) { + sun_label = 0; + other_endian = 0; + return 0; + } + 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 { + heads = SSWAP16(sunlabel->ntrks); + cylinders = SSWAP16(sunlabel->ncyl); + sectors = SSWAP16(sunlabel->nsect); + } + update_units(); + sun_label = 1; + partitions = 8; + return 1; +} + +struct sun_predefined_drives { + char *vendor; + char *model; + unsigned short sparecyl; + unsigned short ncyl; + unsigned short nacyl; + unsigned short pcylcount; + unsigned short ntrks; + unsigned short nsect; + unsigned short rspeed; +} sun_drives[] = { +{"Quantum","ProDrive 80S",1,832,2,834,6,34,3662}, +{"Quantum","ProDrive 105S",1,974,2,1019,6,35,3662}, +{"CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600}, +{"","SUN0104",1,974,2,1019,6,35,3662}, +{"","SUN0207",4,1254,2,1272,9,36,3600}, +{"","SUN0327",3,1545,2,1549,9,46,3600}, +{"","SUN0340",0,1538,2,1544,6,72,4200}, +{"","SUN0424",2,1151,2,2500,9,80,4400}, +{"","SUN0535",0,1866,2,2500,7,80,5400}, +{"","SUN0669",5,1614,2,1632,15,54,3600}, +{"","SUN1.0G",5,1703,2,1931,15,80,3597}, +{"","SUN1.05",0,2036,2,2038,14,72,5400}, +{"","SUN1.3G",6,1965,2,3500,17,80,5400}, +{"","SUN2.1G",0,2733,2,3500,19,80,5400}, +{"IOMEGA","Jaz",0,1019,2,1021,64,32,5394}, +}; + +struct sun_predefined_drives * +sun_autoconfigure_scsi() { + struct sun_predefined_drives *p = NULL; + +#ifdef SCSI_IOCTL_GET_IDLUN + unsigned int id[2]; + char buffer[2048]; + char buffer2[2048]; + FILE *pfd; + char *vendor; + char *model; + char *q; + int i; + + if (!ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id)) { + sprintf(buffer, + "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n", +#if 0 + ((id[0]>>24)&0xff)-/*PROC_SCSI_SCSI+PROC_SCSI_FILE*/33, +#else + /* This is very wrong (works only if you have one HBA), + but I haven't found a way how to get hostno + from the current kernel */ + 0, +#endif + (id[0]>>16)&0xff, + id[0]&0xff, + (id[0]>>8)&0xff); + pfd = fopen("/proc/scsi/scsi","r"); + if (pfd) { + while (fgets(buffer2,2048,pfd)) { + if (!strcmp(buffer, buffer2)) { + if (fgets(buffer2,2048,pfd)) { + q = strstr(buffer2,"Vendor: "); + if (q) { + q += 8; + vendor = q; + q = strstr(q," Model: "); + if (q) { + *q = 0; + q += 8; + model = q; + q = strstr(q," Rev: "); + if (q) { + *q = 0; + for (i = 0; i < SIZE(sun_drives); i++) { + if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor)) + continue; + if (!strstr(model, sun_drives[i].model)) + continue; + printf("Autoconfigure found a %s%s%s\n",sun_drives[i].vendor,(*sun_drives[i].vendor) ? " " : "",sun_drives[i].model); + p = sun_drives + i; + break; + } + } + } + } + } + break; + } + } + fclose(pfd); + } + } +#endif + return p; +} + +void create_sunlabel(void) +{ + struct hd_geometry geometry; + unsigned int ndiv; + int i; + unsigned char c; + struct sun_predefined_drives *p = NULL; + + fprintf(stderr, "Building a new sun disklabel. Changes will remain in memory only,\n" + "until you decide to write them. After that, of course, the previous\n" + "content won't be recoverable.\n\n"); +#if BYTE_ORDER == LITTLE_ENDIAN + other_endian = 1; +#else + other_endian = 0; +#endif + memset(buffer, 0, SECTOR_SIZE); + sunlabel->magic = SSWAP16(SUN_LABEL_MAGIC); + if (!floppy) { + puts("Drive type\n" + " ? auto configure\n" + " 0 custom (with hardware detected defaults)"); + for (i = 0; i < SIZE(sun_drives); i++) { + printf(" %c %s%s%s\n", + i + 'a', sun_drives[i].vendor, + (*sun_drives[i].vendor) ? " " : "", + sun_drives[i].model); + } + for (;;) { + c = read_char("Select type (? for auto, 0 for custom): "); + if (c >= 'a' && c < 'a' + SIZE(sun_drives)) { + p = sun_drives + c - 'a'; + break; + } else if (c >= 'A' && c < 'A' + SIZE(sun_drives)) { + p = sun_drives + c - 'A'; + break; + } else if (c == '0') { + break; + } else if (c == '?' && scsi_disk) { + p = sun_autoconfigure_scsi(); + if (!p) + printf("Autoconfigure failed.\n"); + else + break; + } + } + } + if (!p || floppy) { +#ifdef HDIO_REQ + if (!ioctl(fd, HDIO_REQ, &geometry)) { +#else + if (!ioctl(fd, HDIO_GETGEO, &geometry)) { +#endif + heads = geometry.heads; + sectors = geometry.sectors; + cylinders = geometry.cylinders; + } else { + heads = 0; + sectors = 0; + cylinders = 0; + } + if (floppy) { + sunlabel->nacyl = 0; + sunlabel->pcylcount = SSWAP16(cylinders); + sunlabel->rspeed = SSWAP16(300); + sunlabel->ilfact = SSWAP16(1); + sunlabel->sparecyl = 0; + } else { + heads = read_int(1,heads,1024,0,"Heads"); + sectors = read_int(1,sectors,1024,0,"Sectors/track"); + if (cylinders) + cylinders = read_int(1,cylinders-2,65535,0,"Cylinders"); + else + cylinders = read_int(1,0,65535,0,"Cylinders"); + sunlabel->nacyl = + SSWAP16(read_int(0,2,65535,0, + "Alternate cylinders")); + sunlabel->pcylcount = + SSWAP16(read_int(0,cylinders+SSWAP16(sunlabel->nacyl), + 65535,0,"Physical cylinders")); + sunlabel->rspeed = + SSWAP16(read_int(1,5400,100000,0, + "Rotation speed (rpm)")); + sunlabel->ilfact = + SSWAP16(read_int(1,1,32,0,"Interleave factor")); + sunlabel->sparecyl = + SSWAP16(read_int(0,0,sectors,0, + "Extra sectors per cylinder")); + } + } else { + sunlabel->sparecyl = SSWAP16(p->sparecyl); + sunlabel->ncyl = SSWAP16(p->ncyl); + sunlabel->nacyl = SSWAP16(p->nacyl); + sunlabel->pcylcount = SSWAP16(p->pcylcount); + sunlabel->ntrks = SSWAP16(p->ntrks); + sunlabel->nsect = SSWAP16(p->nsect); + sunlabel->rspeed = SSWAP16(p->rspeed); + cylinders = p->ncyl; + heads = p->ntrks; + sectors = p->nsect; + puts("You may change all the disk params from the x menu"); + } + sprintf(buffer, "%s%s%s cyl %d alt %d hd %d sec %d", + p ? p->vendor : "", (p && *p->vendor) ? " " : "", p ? p->model : (floppy ? "3,5\" floppy" : "Linux custom"), + cylinders, SSWAP16(sunlabel->nacyl), heads, sectors); + sunlabel->ntrks = SSWAP16(heads); + sunlabel->nsect = SSWAP16(sectors); + sunlabel->ncyl = SSWAP16(cylinders); + if (floppy) + set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE); + else { + if (cylinders * heads * sectors >= 150 * 2048) { + ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */ + } else + ndiv = cylinders * 2 / 3; + set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE); + set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP); + sunlabel->infos[1].flags |= 0x01; /* Not mountable */ + } + set_sun_partition(2, 0, cylinders * heads * sectors, WHOLE_DISK); + { + unsigned short *ush = (unsigned short *)sunlabel; + unsigned short csum = 0; + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + } + for (i = 1; i < MAXIMUM_PARTS; i++) + changed[i] = 0; + changed[0] = 1; + get_boot(create_empty); +} + +void toggle_sunflags(int i, unsigned char mask) +{ + if (sunlabel->infos[i].flags & mask) + sunlabel->infos[i].flags &= ~mask; + else sunlabel->infos[i].flags |= mask; + changed[i] = 1; +} + +void fetch_sun(uint *starts, uint *lens, uint *start, uint *stop) +{ + int i, continuous = 1; + *start = 0; *stop = cylinders * heads * sectors; + for (i = 0; i < partitions; i++) { + if (sunlabel->partitions[i].num_sectors && sunlabel->infos[i].id && sunlabel->infos[i].id != WHOLE_DISK) { + starts[i] = SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors; + lens[i] = SSWAP32(sunlabel->partitions[i].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 uint *verify_sun_starts; +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; +} + +void verify_sun(void) +{ + uint starts[8], lens[8], start, stop; + int i,j,k,starto,endo; + int array[8]; + + verify_sun_starts = starts; + fetch_sun(starts,lens,&start,&stop); + for (k = 0; k < 7; k++) { + for (i = 0; i < 8; i++) { + if (k && (lens[i] % (heads * 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 < 8; i++) { + if (lens[i]) + array[i] = i; + else + array[i] = -1; + } + qsort(array,SIZE(array),sizeof(array[0]), + (int (*)(const void *,const void *)) verify_sun_cmp); + if (array[0] == -1) { + printf("No partitions defined\n"); + return; + } + stop = cylinders * heads * 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); +} + +void add_sun_partition(int n, int sys) +{ + uint start, stop, stop2; + uint starts[8], lens[8]; + int whole_disk = 0; + + char mesg[48]; + int i, first, last; + + if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) { + printf("Partition %d is already defined. Delete " + "it before re-adding it.\n", n + 1); + return; + } + + fetch_sun(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; + } + } + sprintf(mesg, "First %s", str_units()); + for (;;) { + if (whole_disk) + first = read_int(0, 0, 0, 0, mesg); + else + first = read_int(scround(start), scround(stop)+1, + scround(stop), 0, mesg); + if (unit_flag) + first *= display_factor; + else + /* Starting sector has to be properly aligned */ + first = (first + heads * sectors - 1) / (heads * sectors); + 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"); + 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 = cylinders * heads * sectors; + stop2 = stop; + for (i = 0; i < partitions; i++) { + if (starts[i] > first && starts[i] < stop) + stop = starts[i]; + } + sprintf(mesg, "Last %s or +size or +sizeM or +sizeK", str_units()); + if (whole_disk) + last = read_int(scround(stop2), scround(stop2), scround(stop2), + 0, mesg); + else if (n == 2 && !first) + last = read_int(scround(first), scround(stop2), scround(stop2), + scround(first), mesg); + else + last = read_int(scround(first), scround(stop), scround(stop), + scround(first), mesg); + if (unit_flag) + last *= display_factor; + if (n == 2 && !first) { + if (last >= stop2) { + whole_disk = 1; + last = stop2; + } else if (last > stop) { + printf ("You haven't covered whole disk with 3rd partition, but your value\n" + "%d %s coveres some other partition. Your entry have been changed\n" + "to %d %s\n", scround(last), str_units(), scround(stop), str_units()); + last = stop; + } + } else if (!whole_disk && last > stop) + last = stop; + + if (whole_disk) sys = WHOLE_DISK; + set_sun_partition(n, first, last, sys); +} + +void +sun_delete_partition(int i) { + if (i == 2 && sunlabel->infos[i].id == WHOLE_DISK && + !sunlabel->partitions[i].start_cylinder && + SSWAP32(sunlabel->partitions[i].num_sectors) + == heads * sectors * 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", + (uint) SSWAP32(sunlabel->partitions[i].num_sectors)); + sunlabel->infos[i].id = 0; + sunlabel->partitions[i].num_sectors = 0; +} + +void +sun_change_sysid(int i, int sys) { + if (sys == LINUX_SWAP && !sunlabel->partitions[i].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; + } + switch (sys) { + case SUNOS_SWAP: + case LINUX_SWAP: + /* swaps are not mountable by default */ + sunlabel->infos[i].flags |= 0x01; + break; + default: + /* assume other types are mountable; + user can change it anyway */ + sunlabel->infos[i].flags &= ~0x01; + break; + } + sunlabel->infos[i].id = sys; +} + +void +sun_list_table(int xtra) { + int i, w; + char *type; + + w = strlen(disk_device); + if (xtra) + printf("\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n" + "%d cylinders, %d alternate cylinders, %d physical cylinders\n" + "%d extra sects/cyl, interleave %d:1\n" + "%s\n" + "Units = %ss of %d * 512 bytes\n\n", + disk_device, heads, sectors, SSWAP16(sunlabel->rspeed), + cylinders, SSWAP16(sunlabel->nacyl), + SSWAP16(sunlabel->pcylcount), + SSWAP16(sunlabel->sparecyl), + SSWAP16(sunlabel->ilfact), + (char *)sunlabel, + str_units(), display_factor); + else + printf("\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n" + "Units = %ss of %d * 512 bytes\n\n", + disk_device, heads, sectors, cylinders, + str_units(), display_factor); + + printf("%*s Flag Start End Blocks Id System\n", + w + 1, "Device"); + for (i = 0 ; i < partitions; i++) { + if (sunlabel->partitions[i].num_sectors) { + __u32 start = SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors; + __u32 len = SSWAP32(sunlabel->partitions[i].num_sectors); + printf( + "%*s%-2d %c%c %9ld %9ld %9ld%c %2x %s\n", +/* device */ w, disk_device, i + 1, +/* flags */ (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ', + (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ', +/* start */ (long) scround(start), +/* end */ (long) scround(start+len), +/* odd flag on end */ (long) len / 2, len & 1 ? '+' : ' ', +/* type id */ sunlabel->infos[i].id, +/* type name */ (type = partition_type(sunlabel->infos[i].id)) + ? type : "Unknown"); + } + } +} + +void +sun_set_alt_cyl(void) { + sunlabel->nacyl = + SSWAP16(read_int(0,SSWAP16(sunlabel->nacyl), 65535, 0, + "Number of alternate cylinders")); +} + +void +sun_set_ncyl(int cyl) { + sunlabel->ncyl = SSWAP16(cyl); +} + +void +sun_set_xcyl(void) { + sunlabel->sparecyl = + SSWAP16(read_int(0, SSWAP16(sunlabel->sparecyl), sectors, 0, + "Extra sectors per cylinder")); +} + +void +sun_set_ilfact(void) { + sunlabel->ilfact = + SSWAP16(read_int(1, SSWAP16(sunlabel->ilfact), 32, 0, + "Interleave factor")); +} + +void +sun_set_rspeed(void) { + sunlabel->rspeed = + SSWAP16(read_int(1, SSWAP16(sunlabel->rspeed), 100000, 0, + "Rotation speed (rpm)")); +} + +void +sun_set_pcylcount(void) { + sunlabel->pcylcount = + SSWAP16(read_int(0, SSWAP16(sunlabel->pcylcount), 65535, 0, + "Number of physical cylinders")); +} + +void +sun_write_table(void) { + unsigned short *ush = (unsigned short *)sunlabel; + unsigned short csum = 0; + + while(ush < (unsigned short *)(&sunlabel->csum)) + csum ^= *ush++; + sunlabel->csum = csum; + if (lseek(fd, 0, SEEK_SET) < 0) + fatal(unable_to_seek); + if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE) + fatal(unable_to_write); +} + diff --git a/fdisk/fdisksunlabel.h b/fdisk/fdisksunlabel.h new file mode 100644 index 000000000..d9d3706d3 --- /dev/null +++ b/fdisk/fdisksunlabel.h @@ -0,0 +1,74 @@ +#include <linux/types.h> /* for __u32 etc */ + +typedef struct { + unsigned char info[128]; /* Informative text string */ + unsigned char spare0[14]; + struct sun_info { + unsigned char spare1; + unsigned char id; + unsigned char spare2; + unsigned char flags; + } infos[8]; + unsigned char spare1[246]; /* Boot information etc. */ + unsigned short rspeed; /* Disk rotational speed */ + unsigned short pcylcount; /* Physical cylinder count */ + unsigned short sparecyl; /* extra sects per cylinder */ + unsigned char spare2[4]; /* More magic... */ + unsigned short ilfact; /* Interleave factor */ + unsigned short ncyl; /* Data cylinder count */ + unsigned short nacyl; /* Alt. cylinder count */ + unsigned short ntrks; /* Tracks per cylinder */ + unsigned short nsect; /* Sectors per track */ + unsigned char spare3[4]; /* Even more magic... */ + struct sun_partition { + __u32 start_cylinder; + __u32 num_sectors; + } partitions[8]; + unsigned short magic; /* Magic number */ + unsigned short csum; /* Label xor'd checksum */ +} sun_partition; + +#define SUN_LABEL_MAGIC 0xDABE +#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA +#define sunlabel ((sun_partition *)buffer) +#define SSWAP16(x) (other_endian ? __swap16(x) \ + : (__u16)(x)) +#define SSWAP32(x) (other_endian ? __swap32(x) \ + : (__u32)(x)) + +#define scround(x) ((x+(display_factor-1)*unit_flag)/display_factor) + +/* fdisk.c */ +extern char changed[MAXIMUM_PARTS]; +extern char buffer[MAX_SECTOR_SIZE]; +extern uint heads, sectors, cylinders; +extern int show_begin; +extern int sun_label; +extern char *partition_type(unsigned char type); +extern void update_units(void); +extern char read_chars(char *mesg); + +/* fdisksunlabel.c */ +#define SUNOS_SWAP 3 +#define WHOLE_DISK 5 + +extern struct systypes sun_sys_types[]; +extern int get_num_sectors(struct sun_partition p); +extern void guess_device_type(int fd); +extern int check_sun_label(void); +extern void sun_nolabel(void); +extern void create_sunlabel(void); +extern void sun_delete_partition(int i); +extern void sun_change_sysid(int i, int sys); +extern void sun_list_table(int xtra); +extern void verify_sun(void); +extern void add_sun_partition(int n, int sys); +extern void sun_write_table(void); +extern void sun_set_alt_cyl(void); +extern void sun_set_ncyl(int cyl); +extern void sun_set_xcyl(void); +extern void sun_set_ilfact(void); +extern void sun_set_rspeed(void); +extern void sun_set_pcylcount(void); +extern void toggle_sunflags(int i, unsigned char mask); + diff --git a/fdisk/llseek.c b/fdisk/llseek.c new file mode 100644 index 000000000..a9cd5a3ac --- /dev/null +++ b/fdisk/llseek.c @@ -0,0 +1,102 @@ +/* + * llseek.c -- stub calling the llseek system call + * + * Copyright (C) 1994 Remy Card. This file may be redistributed + * under the terms of the GNU Public License. + */ + +#include <sys/types.h> + +#include <errno.h> +#include <unistd.h> + +#if defined(__GNUC__) || defined(HAS_LONG_LONG) +typedef long long ext2_loff_t; +#else +typedef long ext2_loff_t; +#endif + +extern ext2_loff_t ext2_llseek (unsigned int, ext2_loff_t, unsigned int); + +#ifdef __linux__ + +#ifdef HAVE_LLSEEK +#include <syscall.h> + +#else /* HAVE_LLSEEK */ + +#ifdef __alpha__ + +#define my_llseek lseek + +#else +#include <linux/unistd.h> /* for __NR__llseek */ + +static int _llseek (unsigned int, unsigned long, + unsigned long, ext2_loff_t *, unsigned int); + +static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high, + unsigned long, offset_low,ext2_loff_t *,result, + unsigned int, origin) + +static ext2_loff_t my_llseek (unsigned int fd, ext2_loff_t offset, + unsigned int origin) +{ + ext2_loff_t result; + int retval; + + retval = _llseek (fd, ((unsigned long long) offset) >> 32, + ((unsigned long long) offset) & 0xffffffff, + &result, origin); + return (retval == -1 ? (ext2_loff_t) retval : result); +} + +#endif /* __alpha__ */ + +#endif /* HAVE_LLSEEK */ + +ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset, + unsigned int origin) +{ + ext2_loff_t result; + static int do_compat = 0; + + if (!do_compat) { + result = my_llseek (fd, offset, origin); + if (!(result == -1 && errno == ENOSYS)) + return result; + + /* + * Just in case this code runs on top of an old kernel + * which does not support the llseek system call + */ + do_compat = 1; + /* + * Now try ordinary lseek. + */ + } + + if ((sizeof(off_t) >= sizeof(ext2_loff_t)) || + (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) + return lseek(fd, (off_t) offset, origin); + + errno = EINVAL; + return -1; +} + +#else /* !linux */ + +ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset, + unsigned int origin) +{ + if ((sizeof(off_t) < sizeof(ext2_loff_t)) && + (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) { + errno = EINVAL; + return -1; + } + return lseek (fd, (off_t) offset, origin); +} + +#endif /* linux */ + + diff --git a/fdisk/sfdisk.8 b/fdisk/sfdisk.8 new file mode 100644 index 000000000..e7f3e82b6 --- /dev/null +++ b/fdisk/sfdisk.8 @@ -0,0 +1,506 @@ +.\" 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 "1 September 1995" "Linux 1.3.23" "Linux Programmer's Manual" +.SH NAME +sfdisk \- Partition table manipulator for Linux +.SH SYNOPSIS +.BR sfdisk " [options] device" +.br +.BR "sfdisk \-s " [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. + +.SS "List Sizes" +.BI "sfdisk \-s " partition +gives the size of +.I partition +in blocks. This may be useful in connection with programs like +.B mkswap +or so. 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 . +.br +.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: +.br +.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 " "[options] device" +will list the partitions on this device. +If the device argument is omitted, the partitions on all hard disks +are listed. +.br +.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 +The trailing \- and + signs indicate that rounding has taken place, +and that the actual value is slightly less (more). +To see the exact values, ask for a listing with sectors as unit. + +.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 \-V option can be used together +with \-l. 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 its 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 \-d 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 \-O option saves is not a binary +version of the output of \-d.) + +There are many options. + +.SH OPTIONS +.TP +.BR \-v " or " \-\-version +Print version number of +.B sfdisk +and exit immediately. +.TP +.BR \-? " or " \-\-help +Print a usage message and exit immediately. +.TP +.BR \-T " or " \-\-list-types +Print the recognized types (system Id's). +.TP +.BR \-s " or " \-\-show\-size +List the size of a partition. +.TP +.BR \-g " or " \-\-show\-geometry +List the kernel's idea of the geometry of the indicated disk(s). +.TP +.BR \-l " or " \-\-list +List the partitions of a device. +.TP +.BR \-d +Dump the partitions of a device in a format useful as input +to sfdisk. 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 " or " \-\-verify +Test whether partitions seem correct. (See above.) +.TP +.BR \-i " or " \-\-increment +Number cylinders etc. starting from 1 instead of 0. +.TP +.BI \-N " number" +Change only the single partition indicated. For example: +.br +.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 +.BI \-A "number" +Make the indicated partition(s) active, and all others inactive. +.TP +.BI \-c " or " \-\-id " number [Id]" +If no Id argument given: print the partition Id of the indicated +partition. If an Id argument is present: change the type (Id) of +the indicated partition to the given value. +This option has the two very long forms \-\-print\-id and \-\-change\-id. +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 \-uS " or " \-uB " or " \-uC " or " \-uM +Accept or report in units of sectors (blocks, cylinders, megabytes, +respectively). The default is cylinders, at least when the geometry +is known. +.TP +.BR \-x " or " \-\-show\-extended +Also list non-primary extended partitions on output, +and expect descriptors for them on input. +.TP +.BI \-C " cylinders" +Specify the number of cylinders, possibly overriding what the kernel thinks. +.TP +.BI \-H " heads" +Specify the number of heads, possibly overriding what the kernel thinks. +.TP +.BI \-S " sectors" +Specify the number of sectors, possibly overriding what the kernel thinks. +.TP +.BR \-f " or " \-\-force +Do what I say, even if it is stupid. +.TP +.BR \-q " or " \-\-quiet +Suppress warning messages. +.TP +.BR \-L " or " \-\-Linux +Do not complain about things irrelevant for Linux. +.TP +.BR \-D " or " \-\-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 \-\-IBM " or " \-\-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 +.B \-R +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, sfdisk 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 \-f +option would force sfdisk to continue even when this test fails.) +.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 \-O 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 +.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 - 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. + +.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. + +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 "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.esfdisk: `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 +A corresponding interactive +.B cfdisk +(with curses interface) is still lacking. +.LP +There are too many options. + +.SH AUTHOR +A. E. Brouwer (aeb@cwi.nl) diff --git a/fdisk/sfdisk.c b/fdisk/sfdisk.c new file mode 100644 index 000000000..c3f470f89 --- /dev/null +++ b/fdisk/sfdisk.c @@ -0,0 +1,2864 @@ +/* + * 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" +#define VERSION "3.07" +#define DATE "980518" + +#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> /* index() */ +#include <ctype.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <linux/unistd.h> /* _syscall */ +#include <linux/hdreg.h> /* HDIO_GETGEO */ +#include <linux/fs.h> /* BLKGETSIZE */ + +#define SIZE(a) (sizeof(a)/sizeof(a[0])) + +/* + * 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 */ +int Linux = 0; /* 1: suppress warnings irrelevant for Linux */ +int DOS = 0; /* 1: shift extended partitions by #sectors, not 1 */ +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; + +void +warn(char *s, ...) { + va_list p; + + va_start(p, s); + fflush(stdout); + if (!quiet) + vfprintf (stderr, s, p); + va_end(p); +} + +void +error(char *s, ...) { + va_list p; + + va_start(p, s); + fflush(stdout); + fprintf(stderr, "\n" PROGNAME ": "); + vfprintf(stderr, s, p); + va_end(p); +} + +void +fatal(char *s, ...) { + va_list p; + + va_start(p, s); + fflush(stdout); + fprintf(stderr, "\n" PROGNAME ": "); + vfprintf(stderr, s, p); + va_end(p); + exit(1); +} + +/* + * A. About seeking + */ + +/* + * sseek: seek to specified sector - return 0 on failure + * + * For >4GB disks lseek needs a > 32bit arg, and we have to use llseek. + * On the other hand, a 32 bit sector number is OK until 2TB. + * The routines _llseek and sseek below are the only ones that + * know about the loff_t type. + */ +#ifndef __alpha__ +static +_syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, + loff_t *, res, uint, wh); +#endif + +int +sseek(char *dev, unsigned int fd, unsigned long s) { + loff_t in, out; + in = ((loff_t) s << 9); + out = 1; + +#ifndef __alpha__ + if (_llseek (fd, in>>32, in & 0xffffffff, &out, SEEK_SET) != 0) { +#else + if ((out = lseek(fd, in, SEEK_SET)) != in) { +#endif + perror("llseek"); + 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", + (uint)(in>>32), (uint)(in & 0xffffffff), + (uint)(out>>32), (uint)(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 sectornumber; + int to_be_written; + char data[512]; +} *sectorhead; + +void +free_sectors(void) { + struct sector *s; + + while (sectorhead) { + s = sectorhead; + sectorhead = s->next; + free(s); + } +} + +struct sector * +get_sector(char *dev, int fd, unsigned 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; + + if (!(s = (struct sector *) malloc(sizeof(struct sector)))) + fatal("out of memory - giving up\n"); + + if (read(fd, s->data, sizeof(s->data)) != sizeof(s->data)) { + 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; +} + +int +msdos_signature (struct sector *s) { + if (*(unsigned short *) (s->data + 0x1fe) != 0xaa55) { + error("ERROR: sector %lu does not have an msdos signature\n", + s->sectornumber); + return 0; + } + return 1; +} + +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; +} + +void +ulong_to_chars(unsigned long u, char *uu) { + int i; + + for(i=0; i<4; i++) { + uu[i] = (u & 0xff); + u >>= 8; + } +} + +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; +} + +int +save_sectors(char *dev, int fdin) { + struct sector *s; + char ss[516]; + int fdout; + + 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); + return 0; + } + + for (s = sectorhead; s; s = s->next) + if (s->to_be_written) { + ulong_to_chars(s->sectornumber, ss); + if (!sseek(dev, fdin, s->sectornumber)) + return 0; + if (read(fdin, ss+4, 512) != 512) { + perror("read"); + error("read error on %s - cannot read sector %lu\n", + dev, s->sectornumber); + return 0; + } + if (write(fdout, ss, sizeof(ss)) != sizeof(ss)) { + perror("write"); + error("write error on %s\n", save_sector_file); + return 0; + } + } + return 1; +} + +void reread_disk_partition(char *dev, int fd); + +int +restore_sectors(char *dev) { + int fdin, fdout, ct; + struct stat statbuf; + char *ss0, *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); + return 0; + } + if (statbuf.st_size % 516) { + error("partition restore file has wrong size - not restoring\n"); + return 0; + } + if (!(ss = (char *) malloc(statbuf.st_size))) { + error("out of memory?\n"); + return 0; + } + 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); + return 0; + } + if (read(fdin, ss, statbuf.st_size) != statbuf.st_size) { + perror("read"); + error("error reading %s\n", restore_sector_file); + return 0; + } + + fdout = open(dev, O_WRONLY); + if (fdout < 0) { + perror(dev); + error("cannot open device %s for writing\n", dev); + return 0; + } + + ss0 = ss; + ct = statbuf.st_size/516; + while(ct--) { + sno = chars_to_ulong(ss); + if (!sseek(dev, fdout, sno)) + return 0; + if (write(fdout, ss+4, 512) != 512) { + perror(dev); + error("error writing sector %lu on %s\n", sno, dev); + return 0; + } + ss += 516; + } + free(ss0); + + reread_disk_partition(dev, fdout); + + return 1; +} + +/* + * 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; + * }; + */ + +unsigned long cylindersize; +unsigned long heads, sectors, cylinders; +unsigned long specified_heads, specified_sectors, specified_cylinders; + +void +get_cylindersize(char *dev, int fd, int silent) { + struct hd_geometry g; + int ioctl_ok = 0; + + heads = sectors = cylinders = 0; + + if (!ioctl(fd, HDIO_GETGEO, &g)) { + ioctl_ok = 1; + + heads = g.heads; + sectors = g.sectors; + cylinders = g.cylinders; + } + + if (specified_heads) + heads = specified_heads; + if (specified_sectors) + sectors = specified_sectors; + if (specified_cylinders) + cylinders = specified_cylinders; + + cylindersize = heads * sectors; + + if (ioctl_ok) { + if (g.start && !force) { + warn( + "Warning: start=%d - 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", g.start); + exit(1); + } + if (heads != g.heads) + warn("Warning: HDIO_GETGEO says that there are %d heads\n", + g.heads); + if (sectors != g.sectors) + warn("Warning: HDIO_GETGEO says that there are %d sectors\n", + g.sectors); + if (cylinders != g.cylinders) + warn("Warning: HDIO_GETGEO says that there are %d cylinders\n", + g.cylinders); + } else if (!silent) + if (!heads || !sectors || !cylinders) + printf("Disk %s: cannot get geometry\n", dev); + if (sectors > 63) + warn("Warning: unlikely number of sectors (%d) - usually at most 63\n" + "This will give problems with all software that uses C/H/S addressing.\n", + sectors); + if (!silent) + printf("\nDisk %s: %lu heads, %lu sectors, %lu cylinders\n", + dev, heads, sectors, cylinders); +} + +typedef struct { unsigned char h,s,c; } chs; /* has some c bits in s */ +chs zero_chs = { 0,0,0 }; + +typedef struct { unsigned long h,s,c; } longchs; +longchs zero_longchs; + +chs +longchs_to_chs (longchs aa) { + 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 (heads && sectors) { + a.h = heads - 1; + a.s = sectors | 0xc0; + a.c = 0xff; + } else + a = zero_chs; + return a; +} + +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; +} + +longchs +ulong_to_longchs (unsigned long sno) { + longchs aa; + + if (heads && sectors && cylindersize) { + aa.s = 1 + sno % sectors; + aa.h = (sno / sectors) % heads; + aa.c = sno / cylindersize; + return aa; + } else { + return zero_longchs; + } +} + +unsigned long +longchs_to_ulong (longchs aa) { + return (aa.c*cylindersize + aa.h*sectors + aa.s - 1); +} + +chs +ulong_to_chs (unsigned long sno) { + return longchs_to_chs(ulong_to_longchs(sno)); +} + +unsigned long +chs_to_ulong (chs a) { + return longchs_to_ulong(chs_to_longchs(a)); +} + +int +is_equal_chs (chs a, chs b) { + return (a.h == b.h && a.s == b.s && a.c == b.c); +} + +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 (heads && aa.h >= heads) { + warn("%s of partition %s has impossible value for head: " + "%d (should be in 0-%d)\n", w, v, aa.h, heads-1); + ret = 0; + } + if (sectors && (aa.s == 0 || aa.s > sectors)) { + warn("%s of partition %s has impossible value for sector: " + "%d (should be in 1-%d)\n", w, v, aa.s, sectors); + ret = 0; + } + if (cylinders && aa.c >= cylinders) { + warn("%s of partition %s has impossible value for cylinders: " + "%d (should be in 0-%d)\n", w, v, aa.c, 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 + +/* + * List of system Id's, adapted from fdisk 2.0d and <linux/genhd.h> + * and SFS and several other sources. + */ +struct systypes { + unsigned char type; + char *name; +} sys_types[] = { + {0, "Empty"}, + {1, "DOS 12-bit FAT"}, /* Primary DOS with 12-bit FAT */ + {2, "XENIX /"}, /* XENIX / filesystem */ + {3, "XENIX /usr"}, /* XENIX /usr filesystem */ + {4, "DOS 16-bit FAT <32M"}, /* Primary DOS with 16-bit FAT */ + {5, "DOS Extended"}, /* DOS 3.3+ extended partition */ + {6, "DOS 16-bit FAT >=32M"}, + {7, "HPFS / NTFS"}, + {8, "AIX boot or SplitDrive"}, + {9, "AIX data or Coherent"}, + {0x0a, "OS/2 Boot Manager"}, + {0x0b, "Win95 FAT32"}, + {0x0c, "Win95 FAT32 (LBA)"}, + {0x0e, "Win95 FAT16 (LBA)"}, + {0x0f, "Win95 Extended (LBA)"}, + {0x10, "OPUS"}, + {0x11, "Hidden DOS FAT12"}, + {0x12, "Compaq diagnostics"}, + {0x14, "Hidden DOS FAT16"}, + {0x16, "Hidden DOS FAT16 (big)"}, + {0x17, "Hidden HPFS/NTFS"}, + {0x18, "AST Windows swapfile"}, + {0x24, "NEC DOS"}, + {0x3c, "PartitionMagic recovery"}, + {0x40, "Venix 80286"}, + {0x41, "Linux/MINIX (sharing disk with DRDOS)"}, + {0x42, "SFS or Linux swap (sharing disk with DRDOS)"}, + {0x43, "Linux native (sharing disk with DRDOS)"}, + {0x50, "DM (disk manager)"}, + {0x51, "DM6 Aux1 (or Novell)"}, + {0x52, "CP/M or Microport SysV/AT"}, + {0x53, "DM6 Aux3"}, + {0x54, "DM6"}, + {0x55, "EZ-Drive (disk manager)"}, + {0x56, "Golden Bow (disk manager)"}, + {0x5c, "Priam Edisk (disk manager)"}, + {0x61, "SpeedStor"}, + {0x63, "GNU HURD or Mach or Sys V/386 (such as ISC UNIX)"}, + {0x64, "Novell Netware 286"}, + {0x65, "Novell Netware 386"}, + {0x70, "DiskSecure Multi-Boot"}, + {0x75, "PC/IX"}, + {0x77, "QNX4.x"}, + {0x78, "QNX4.x 2nd part"}, + {0x79, "QNX4.x 3rd part"}, + {0x80, "MINIX until 1.4a"}, + {0x81, "MINIX / old Linux"}, + {0x82, "Linux swap"}, + {0x83, "Linux native"}, + {0x84, "OS/2 hidden C: drive"}, + {0x85, "Linux extended"}, + {0x86, "NTFS volume set"}, + {0x87, "NTFS volume set"}, + {0x93, "Amoeba"}, + {0x94, "Amoeba BBT"}, /* (bad block table) */ + {0xa0, "IBM Thinkpad hibernation"}, /* according to dan@fch.wimsey.bc.ca */ + {0xa5, "BSD/386"}, /* 386BSD */ + {0xa6, "OpenBSD"}, + {0xa7, "NeXTSTEP 486"}, + {0xb7, "BSDI fs"}, + {0xb8, "BSDI swap"}, + {0xc1, "DRDOS/sec (FAT-12)"}, + {0xc4, "DRDOS/sec (FAT-16, < 32M)"}, + {0xc6, "DRDOS/sec (FAT-16, >= 32M)"}, + {0xc7, "Syrinx"}, + {0xdb, "CP/M or Concurrent CP/M or Concurrent DOS or CTOS"}, + {0xe1, "DOS access or SpeedStor 12-bit FAT extended partition"}, + {0xe3, "DOS R/O or SpeedStor"}, + {0xe4, "SpeedStor 16-bit FAT extended partition < 1024 cyl."}, + {0xeb, "BeOS fs"}, + {0xf1, "SpeedStor"}, + {0xf2, "DOS 3.3+ secondary"}, + {0xf4, "SpeedStor large partition"}, + {0xfe, "SpeedStor >1024 cyl. or LANstep"}, + {0xff, "Xenix Bad Block Table"} +}; + + +const char * +sysname(unsigned char type) { + struct systypes *s; + + for (s = sys_types; s - sys_types < SIZE(sys_types); s++) + if (s->type == type) + return s->name; + return "Unknown"; +} + +void +list_types(void) { + struct systypes *s; + + printf("Id Name\n\n"); + for (s = sys_types; s - sys_types < SIZE(sys_types); s++) + printf("%2x %s\n", s->type, s->name); +} + +int +is_extended(unsigned char type) { + return (type == EXTENDED_PARTITION + || type == LINUX_EXTENDED + || type == WIN98_EXTENDED); +} + +int +is_bsd(unsigned char type) { + return (type == BSD_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 */ +}; + +/* Unfortunately, partitions are not aligned, and non-Intel machines + are unhappy with non-aligned integers. So, we need a copy by hand. */ +int +copy_to_int(unsigned char *cp) { + unsigned int m; + + m = *cp++; + m += (*cp++ << 8); + m += (*cp++ << 16); + m += (*cp++ << 24); + return m; +} + +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); +} + +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(cp); + p->nr_sects = copy_to_int(cp+4); +} + +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 start; + unsigned long size; + unsigned 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; + +struct part_desc * +outer_extended_partition(struct part_desc *p) { + while (p->ep) + p = p->ep; + return p; +} + +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[128]; + int partno; +} oldp, newp; + +/* determine where on the disk this information goes */ +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 */ +int reread_ioctl(int fd) { + if(ioctl(fd, BLKRRPART)) { + perror("BLKRRPART"); + return -1; + } + return 0; +} + +/* reread after writing */ +void +reread_disk_partition(char *dev, int fd) { + printf("Re-reading the partition table ...\n"); + fflush(stdout); + sync(); + sleep(3); /* superfluous since 1.3.20 */ + + if(reread_ioctl(fd)) + printf("The command to re-read the partition table failed\n" + "Reboot your system now, before using mkfs\n"); + + if (close(fd)) { + perror(dev); + printf("Error closing %s\n", dev); + } + printf("\n"); +} + +/* find Linux name of this partition, assuming that it will have a name */ +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; +} + +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; +} + +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)) + fatal("%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; + +void +set_format(char c) { + switch(c) { + default: + printf("unrecognized format - using sectors\n"); + 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; + } +} + +unsigned long +unitsize(int format) { + default_format = (cylindersize ? F_CYLINDER : F_MEGABYTE); + if (!format && !(format = specified_format)) + format = default_format; + + switch(format) { + default: + case F_CYLINDER: + if(cylindersize) return cylindersize; + case F_SECTOR: + return 1; + case F_BLOCK: + return 2; + case F_MEGABYTE: + return 2048; + } +} + +unsigned long +get_disksize(int format) { + unsigned long cs = cylinders; + if (cs && leave_last) + cs--; + return (cs * cylindersize) / unitsize(format); +} + +void +out_partition_header(char *dev, int format) { + if (dump) { + printf("# partition table of %s\n", dev); + printf("unit: sectors\n\n"); + return; + } + + default_format = (cylindersize ? F_CYLINDER : F_MEGABYTE); + if (!format && !(format = specified_format)) + format = default_format; + + switch(format) { + default: + printf("unimplemented format - using %s\n", + cylindersize ? "cylinders" : "sectors"); + case F_CYLINDER: + if (cylindersize) { + printf("Units = cylinders of %lu bytes, blocks of 1024 bytes" + ", counting from %d\n\n", + 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 = megabytes of 1048576 bytes, blocks of 1024 bytes" + ", counting from %d\n\n", increment); + printf(" Device Boot Start End MB #blocks Id System\n"); + break; + } +} + +static void +out_rounddown(int width, unsigned long n, unsigned long unit, int inc) { + printf("%*lu", width, inc + n/unit); + if (unit != 1) + putchar((n % unit) ? '+' : ' '); + putchar(' '); +} + +static void +out_roundup(int width, unsigned long n, unsigned long unit, int inc) { + if (n == (unsigned long)(-1)) + printf("%*s", width, "-"); + else + printf("%*lu", width, inc + n/unit); + if (unit != 1) + putchar(((n+1) % unit) ? '-' : ' '); + putchar(' '); +} + +static void +out_roundup_size(int width, unsigned long n, unsigned long unit) { + printf("%*lu", width, (n+unit-1)/unit); + if (unit != 1) + putchar((n % unit) ? '-' : ' '); + putchar(' '); +} + +void +out_partition(char *dev, int format, struct part_desc *p, struct disk_desc *z) { + unsigned 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("%8s%-2u", dev, lpno); /* 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=%9lu", start); + printf(", size=%8lu", 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 (cylindersize) { + out_rounddown(6, start, cylindersize, increment); + out_roundup(6, end, cylindersize, increment); + out_roundup_size(6, size, cylindersize); + out_rounddown(8, size, 2, 0); + break; + } + /* fall through */ + default: + case F_SECTOR: + out_rounddown(9, start, 1, increment); + out_roundup(9, end, 1, increment); + out_rounddown(9, 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(8, 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(8, 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) : 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)) + printf("\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) : 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)) + printf("\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(cylinders && cylinders < 1024 && bb.c > cylinders) + printf("partition ends on cylinder %ld, beyond the end of the disk\n", + bb.c); + } +} + +void +out_partitions(char *dev, struct disk_desc *z) { + int pno, format = 0; + + if (z->partno == 0) + printf("No partitions found\n"); + else { + out_partition_header(dev, format); + for(pno=0; pno < z->partno; pno++) { + out_partition(dev, format, &(z->partitions[pno]), z); + 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)); +} + +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; +} + +int +partitions_ok(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) + fatal("no partition table present.\n"); + else + fatal("strange, only %d partitions defined.\n", 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) + warn("Warning: partition %s has size 0 but is not marked Empty\n", + PNO(p)); + else if(p->p.bootable != 0) + warn("Warning: partition %s has size 0 and is bootable\n", + PNO(p)); + else if(p->p.start_sect != 0) + 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) { + warn("Warning: partition %s ", PNO(p)); + warn("is not contained in partition %s\n", 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))) { + warn("Warning: partitions %s ", PNO(p)); + warn("and %s overlap\n", 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) { + warn("Warning: partition %s contains part of ", PNO(p)); + warn("the partition table (sector %lu),\n", q->start); + warn("and will destroy it when filled\n"); + return 0; + } + + /* Do they start past zero and end before end-of-disk? */ + { unsigned long ds = get_disksize(F_SECTOR); + for (p = partitions; p < partitions+partno; p++) + if (p->size) { + if(p->start == 0) { + warn("Warning: partition %s starts at sector 0\n", PNO(p)); + return 0; + } + if (p->size && p->start + p->size > ds) { + warn("Warning: partition %s extends past end of disk\n", PNO(p)); + 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) { + warn("Among the primary partitions, at most one can be extended\n"); + warn(" (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 (cylindersize) { + for(p = partitions; p < partitions+partno; p++) + if (p->size) { + if(p->start % cylindersize != 0 + && (!p->ep || p->start / cylindersize != p->ep->start / cylindersize) + && (p->p.start_sect >= cylindersize)) { + warn("Warning: partition %s does not start " + "at a cylinder boundary\n", PNO(p)); + if (!Linux) + return 0; + } + if((p->start + p->size) % cylindersize) { + warn("Warning: partition %s does not end " + "at a cylinder boundary\n", PNO(p)); + if (!Linux) + 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) { + 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) { + warn("Warning: usually one can boot from primary partitions " + "only\n" "LILO disregards the `bootable' flag.\n"); + break; + } + } + if (pno == -1 || pno >= 4) + 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) : zero_chs; + b = p->p.begin_chs; + aa = chs_to_longchs(a); + bb = chs_to_longchs(b); + if (!chs_ok(b, PNO(p), "start")) + return 0; + if(a.s && !is_equal_chs(a, b)) + 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) : zero_chs; + b = p->p.end_chs; + aa = chs_to_longchs(a); + bb = chs_to_longchs(b); + if (!chs_ok(b, PNO(p), "end")) + return 0; + if(a.s && !is_equal_chs(a, b)) + 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(cylinders && cylinders < 1024 && bb.c > cylinders) + 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 start, here, next; + int i, moretodo = 1; + struct partition p; + struct part_desc *partitions = &(z->partitions[0]); + int pno = z->partno; + + here = start = ep->start; + + while (moretodo) { + moretodo = 0; + + if (!(s = get_sector(dev, fd, here))) + break; + + if (!msdos_signature(s)) + break; + + cp = s->data + 0x1be; + + if (pno+4 >= SIZE(z->partitions)) { + printf("too many partitions - ignoring those past nr (%d)\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)) { + if (next) + printf("tree of partitions?\n"); + partitions[pno].start = next = start + p.start_sect; + 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 8 +#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 start = ep->start; + struct sector *s; + struct part_desc *partitions = &(z->partitions[0]); + int pno = z->partno; + + if (!(s = get_sector(dev,fd,start+1))) + return; + l = (struct bsd_disklabel *) (s->data); + if (l->d_magic != BSD_DISKMAGIC) + return; + + bp = bp0 = &l->d_partitions[0]; + while (bp - bp0 <= BSD_MAXPARTITIONS) { + if (pno+1 >= SIZE(z->partitions)) { + printf("too many partitions - ignoring those " + "past nr (%d)\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; + + 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) { + printf("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))) { + printf("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) { + printf("strange..., an extended partition of size 0?\n"); + continue; + } + extended_partition(dev, fd, &partitions[i], z); + } + if (is_bsd(partitions[i].p.sys_type)) { + if (!partitions[i].size) { + printf("strange..., a BSD partition of size 0?\n"); + continue; + } + bsd_partition(dev, fd, &partitions[i], z); + } + } + return 1; +} + +static int +osf_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) { + return 0; +} + +static int +sun_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) { + return 0; +} + +static int +amiga_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) { + return 0; +} + +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)) { + printf(" %s: unrecognized partition\n", dev); + return; + } +} + +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) { + printf("-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; + copy_from_part(&(p->p), s->data + p->offset); + *(unsigned short *)(&(s->data[0x1fe])) = 0xaa55; + } + if (save_sector_file) { + if (!save_sectors(dev, fd)) { + fatal("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; + } + 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) + +int +read_stdin(unsigned char **fields, unsigned char *line, int fieldssize, int linesize) { + unsigned 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, stdin); + if (lp == NULL) { + eof = 1; + return RD_EOF; + } + if (!(lp = index(lp, '\n'))) + fatal("long or incomplete input line - quitting\n"); + *lp = 0; + + /* remove comments, if any */ + if ((lp = index(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 = index(line+2, ':')) != 0) { + struct dumpfld *d; + + nxtfld: + ip++; + while(isspace(*ip)) + ip++; + if (*ip == 0) + return fno; + for(d = dumpflds; d-dumpflds < 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 + fatal("input error: `=' expected after %s field\n", + d->fldname); + if (fno <= d->fldno) + fno = d->fldno + 1; + if(*ip == 0) + return fno; + if(*ip != ',' && *ip != ';') + fatal("input error: unexpected character %c after %s field\n", + *ip, d->fldname); + *ip = 0; + goto nxtfld; + } + } + fatal("unrecognized input: %s\n", 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 */ +int +get_ul(char *u, unsigned long *up, unsigned long def, int base) { + char *nu; + + if (*u) { + errno = 0; + *up = strtoul(u, &nu, base); + if (errno == ERANGE) { + printf("number too big\n"); + return -1; + } + if (*nu) { + printf("trailing junk after number\n"); + return -1; + } + } 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 */ +unsigned long +first_free(int pno, int is_extended, struct part_desc *ep, int format, + unsigned long mid, struct disk_desc *z) { + unsigned 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 */ +unsigned long +max_length(int pno, int is_extended, struct part_desc *ep, int format, + unsigned long start, struct disk_desc *z) { + unsigned 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 */ +/* ep is 0 or points to surrounding extended partition */ +int +compute_start_sect(struct part_desc *p, struct part_desc *ep) { + unsigned long base; + int inc = (DOS && sectors) ? sectors : 1; + int 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) { + p->start -= delta; + p->size += delta; + if (is_extended(p->p.sys_type) && boxes == ONESECTOR) + p->size = inc; + else if ((int)(p->size) <= 0) { + 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); + p->p.end_chs = ulong_to_chs(p->start + p->size - 1); + } + p->p.nr_sects = p->size; + return 1; +} + +/* build the extended partition surrounding a given logical partition */ +int +build_surrounding_extended(struct part_desc *p, struct part_desc *ep, + struct disk_desc *z) { + int inc = (DOS && sectors) ? 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) { + 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; +} + +int +read_line(int pno, struct part_desc *ep, char *dev, int interactive, + struct disk_desc *z) { + unsigned char line[1000]; + unsigned char *fields[11]; + int fno, pct = pno%4; + struct part_desc p, *orig; + unsigned 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)) + warn("\n"); + warn("%8s%d: ", dev, lpno); + } + + /* read input line - skip blank lines when reading from a file */ + do { + fno = read_stdin(fields, line, SIZE(fields), SIZE(line)); + } while(fno == RD_CMD || (fno == 0 && !interactive)); + if (fno == RD_EOF) { + return -1; + } else if (fno > 10 && *(fields[10]) != 0) { + printf("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 (cylindersize && specified_format == F_MEGABYTE) + 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 */ + 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_ul(fields[2], &ul, LINUX_NATIVE, 16)) + return 0; + if (ul > 255) { + 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_ul(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 (get_ul(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) { + warn("Warning: exceeds max allowable size (%lu)\n", ml1 / unitsize(0)); + if (!force) + return 0; + } + if (p.size == 0 && pno >= 4 && (fno < 2 || !*(fields[1]))) { + 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) { + 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 { + 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) { + 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); + } + { longchs aa = chs_to_longchs(p.p.end_chs), bb; + + if (fno < 8) { + bb = aa; + } else if (fno < 10) { + 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); + } + + if (pno > 3 && p.size && show_extended && p.p.sys_type != EMPTY_PARTITION + && (is_extended(p.p.sys_type) != (pct == 1))) { + 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); + + 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 */ +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) + fatal("bad input\n"); + if (i < 0) { + p->ep = ep; + return 0; + } + + ret: + p->ep = ep; + if (pno >= z->partno) + z->partno += 4; + return 1; +} + +void +read_partition_chain(char *dev, int interactive, struct part_desc *ep, + struct disk_desc *z) { + int i, base; + + eob = 0; + while (1) { + base = z->partno; + if (base+4 > SIZE(z->partitions)) { + printf("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; + } + } +} + +void +read_input(char *dev, int interactive, struct disk_desc *z) { + int i; + struct part_desc *partitions = &(z->partitions[0]), *ep; + + for (i=0; i < SIZE(z->partitions); i++) + partitions[i] = zero_part_desc; + z->partno = 0; + + if (interactive) + warn(" +Input in the following format; absent fields get a default value. +<start> <size> <type [E,S,L,X,hex]> <bootable [-,*]> <c,h,s> <c,h,s> +Usually you only need to specify <start> and <size> (and perhaps <type>). +"); + 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 version(void) { + printf(PROGNAME " version " VERSION " (aeb@cwi.nl, " DATE ")\n"); +} + +static void +usage(void) { + version(); + printf("Usage: + " PROGNAME " [options] device ... +device: something like /dev/hda or /dev/sda +useful options: + -s [or --show-size]: list size of a partition + -c [or --id]: print or change partition Id + -l [or --list]: list partitions of each device + -d [or --dump]: idem, but in a format suitable for later input + -i [or --increment]: number cylinders etc. from 1 instead of from 0 + -uS, -uB, -uC, -uM: accept/report in units of sectors/blocks/cylinders/MB + -T [or --list-types]:list the known partition types + -D [or --DOS]: for DOS-compatibility: waste a little space + -R [or --re-read]: make kernel reread partition table + -N# : change only the partition with number # + -n : do not actually write to disk + -O file : save the sectors that will be overwritten to file + -I file : restore these sectors again + -v [or --version]: print version + -? [or --help]: print this message +dangerous options: + -g [or --show-geometry]: print the kernel's idea of the geometry + -x [or --show-extended]: also list extended partitions on output + or expect descriptors for them on input + -L [or --Linux]: do not complain about things irrelevant for Linux + -q [or --quiet]: suppress warning messages + You can override the detected geometry using: + -C# [or --cylinders #]:set the number of cylinders to use + -H# [or --heads #]: set the number of heads to use + -S# [or --sectors #]: set the number of sectors to use + You can disable all consistency checking with: + -f [or --force]: do what I say, even if it is stupid +"); + exit(1); +} + +static void +activate_usage(char *progn) { + printf("Usage: + %s device list active partitions on device + %s device n1 n2 ... activate partitions n1 ..., inactivate the rest + %s device activate partition n, inactivate the other ones +", progn, progn, PROGNAME " -An"); + exit(1); +} + +static void +unhide_usage(char *progn) { + exit(1); +} + +static char short_opts[] = "cdfgilnqsu:vx?1A::C:DH:I:LN:O:RS:TU::V"; + +#define PRINT_ID 0400 +#define CHANGE_ID 01000 + +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' }, + { "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' }, + { "help", no_argument, NULL, '?' }, + { "one-only", no_argument, NULL, '1' }, + { "cylinders", required_argument, NULL, 'C' }, + { "heads", required_argument, NULL, 'H' }, + { "sectors", required_argument, NULL, 'S' }, + { "activate", optional_argument, NULL, 'A' }, + { "DOS", no_argument, NULL, 'D' }, + { "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, 160 }, + { "IBM", no_argument, NULL, 161 }, + { "leave-last", no_argument, NULL, 161 }, +/* undocumented flags - not all completely implemented */ + { "in-order", no_argument, NULL, 128 }, + { "not-in-order", no_argument, NULL, 129 }, + { "inside-outer", no_argument, NULL, 130 }, + { "not-inside-outer", no_argument, NULL, 131 }, + { "nested", no_argument, NULL, 132 }, + { "chained", no_argument, NULL, 133 }, + { "onesector", no_argument, NULL, 134 }, + { NULL, 0, NULL, 0 } +}; + +/* default devices to list */ +static struct devd { + char *pref, *letters; +} defdevs[] = { + { "hd", "abcdefgh" }, + { "sd", "abcde" }, + { "xd", "ab" }, + { "ed", "abcd" } +}; + +int +is_ide_cdrom(char *device) { + /* No device was given explicitly, and we are trying some + likely things. But opening /dev/hdc may produce errors like + "hdc: tray open or drive not ready" + if it happens to be a CD-ROM drive. So try to be careful. + This only works since 2.1.73. */ + + FILE *procf; + char buf[100]; + struct stat statbuf; + + sprintf(buf, "/proc/ide/%s/media", device+5); + procf = fopen(buf, "r"); + if (procf != NULL && fgets(buf, sizeof(buf), procf)) + return !strncmp(buf, "cdrom", 5); + + /* Now when this proc file does not exist, skip the + device when it is read-only. */ + if (stat(device, &statbuf) == 0) + return (statbuf.st_mode & 0222) == 0; + + return 0; +} + +void do_list(char *dev, int silent); +void do_size(char *dev, int silent); +void do_geom(char *dev, int silent); +void do_fdisk(char *dev); +void do_reread(char *dev); +void do_change_id(char *dev, char *part, char *id); +void do_unhide(char **av, int ac, char *arg); +void do_activate(char **av, int ac, char *arg); + +int 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_reread = 0; + int activate = 0; + int do_id = 0; + int unhide = 0; + int fdisk = 0; + char *activatearg = 0; + char *unhidearg = 0; + + if (argc < 1) + fatal("no command?\n"); + if ((progn = rindex(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 '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': + version(); + exit(0); + case 'x': + show_extended = 1; break; + case 'A': + activatearg = optarg; + activate = 1; break; + case 'C': + specified_cylinders = atoi(optarg); break; + case 'D': + DOS = 1; break; + case 'H': + specified_heads = atoi(optarg); break; + case 'L': + Linux = 1; break; + case 'N': + one_only = atoi(optarg); break; + case 'I': + restore_sector_file = optarg; break; + case 'O': + save_sector_file = optarg; break; + case 'R': + opt_reread = 1; break; + case 'S': + specified_sectors = atoi(optarg); break; + case 'T': + list_types(); + exit(0); + case 'U': + unhidearg = optarg; + unhide = 1; break; + case 'V': + verify = 1; break; + case '?': + default: + usage(); break; + + /* undocumented flags */ + case 128: + partitions_in_order = 1; break; + case 129: + partitions_in_order = 0; break; + case 130: + all_logicals_inside_outermost_extended = 1; break; + case 131: + all_logicals_inside_outermost_extended = 0; break; + case 132: + boxes = NESTED; break; + case 133: + boxes = CHAINED; break; + case 134: + boxes = ONESECTOR; break; + + /* more flags */ + case 160: + no_reread = 1; break; + case 161: + leave_last = 1; break; + } + } + + if (optind == argc && (opt_list || opt_out_geom || opt_size || verify)) { + struct devd *dp; + char *lp; + char device[10]; + + total_size = 0; + + for(dp = defdevs; dp-defdevs < SIZE(defdevs); dp++) { + lp = dp->letters; + while(*lp) { + sprintf(device, "/dev/%s%c", dp->pref, *lp++); + if (!strcmp(dp->pref, "hd") && is_ide_cdrom(device)) + continue; + if (opt_out_geom) + do_geom(device, 1); + if (opt_size) + do_size(device, 1); + if (opt_list || verify) + do_list(device, 1); + } + } + + if (opt_size) + printf("total: %d 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(); + } + + if (opt_list || opt_out_geom || opt_size || verify) { + while (optind < argc) { + if (opt_out_geom) + do_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 (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) + fatal("usage: sfdisk --print-id device partition-number\n"); + else if ((do_id & CHANGE_ID) != 0 && optind != argc-3) + fatal("usage: sfdisk --change-id device partition-number Id\n"); + else if (optind != argc-3 && optind != argc-2) + fatal("usage: sfdisk --id device partition-number [Id]\n"); + do_change_id(argv[optind], argv[optind+1], + (optind == argc-2) ? 0 : argv[optind+2]); + exit(exit_status); + } + + if (optind != argc-1) + fatal("can specify only one device (except with -l or -s)\n"); + 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 + */ + +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); + fatal("cannot open %s %s\n", dev, rw ? "read-write" : "for reading"); + } + return fd; +} + +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(z)) + warn("%s: OK\n", dev); + else + exit_status = 1; + } +} + +void +do_geom (char *dev, int silent) { + int fd; + struct hd_geometry g; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + + /* get_cylindersize(dev, fd, silent); */ + if (!ioctl(fd, HDIO_GETGEO, &g)) + printf("%s: %d cylinders, %d heads, %d sectors/track\n", + dev, g.cylinders, g.heads, g.sectors); + else + printf("%s: unknown geometry\n", dev); +} + +/* for compatibility with earlier fdisk: provide option -s */ +void +do_size (char *dev, int silent) { + int fd, size; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + + if(ioctl(fd, BLKGETSIZE, &size)) { + if(!silent) { + perror(dev); + fatal("BLKGETSIZE ioctl failed for %s\n", dev); + } + return; + } + + size /= 2; /* convert sectors to blocks */ + + /* a CDROM drive without mounted CD yields MAXINT */ + if (silent && size == ((1<<30)-1)) + return; + + if (silent) + printf("%s: %9d\n", dev, size); + else + printf("%d\n", size); + + total_size += size; +} + +/* + * 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. + */ +void +set_active (struct disk_desc *z, char *pnam) { + int pno; + + pno = asc_to_index(pnam, z); + z->partitions[pno].p.bootable = 0x80; +} + +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%d\n", dev, lpno); + else + printf("%s#%d\n", dev, pno); + if (z->partitions[pno].p.bootable != 0x80) + 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++) + 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)) + 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) + 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); +} + +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 + fatal("partition %s has id %x and is not hidden\n", pnam, id); + z->partitions[pno].p.sys_type = id; +} + +/* + * maybe remove and make part of --change-id + */ +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)) + warn("Done\n\n"); + else + exit_status = 1; +} + +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); + return; + } + i = strtoul(id, NULL, 16); + if (i > 255) + fatal("Bad Id %x\n", i); + z->partitions[pno].p.sys_type = i; + + if(write_partitions(dev, fd, z)) + warn("Done\n\n"); + else + exit_status = 1; +} + +void +do_reread(char *dev) { + int fd; + + fd = my_open(dev, 0, 0); + if(reread_ioctl(fd)) + printf("This disk is currently in use.\n"); +} + +/* + * I. Writing the new situation + */ + +void +do_fdisk(char *dev){ + int fd; + int c, answer; + struct stat statbuf; + int interactive = isatty(0); + struct disk_desc *z; + + if (stat(dev, &statbuf) < 0) { + perror(dev); + fatal("Fatal error: cannot find %s\n", dev); + } + if (!S_ISBLK(statbuf.st_mode)) { + warn("Warning: %s is not a block device\n", dev); + } + fd = my_open(dev, !no_write, 0); + + if(!no_write && !no_reread) { + warn("Checking that no-one is using this disk right now ...\n"); + if(reread_ioctl(fd)) { + printf(" +This disk is currently in use - repartitioning is probably a bad idea. +Umount all file systems, and swapoff all swap partitions on this disk. +Use the --no-reread flag to suppress this check.\n"); + if (!force) { + printf("Use the --force flag to overrule all checks.\n"); + exit(1); + } + } else + warn("OK"); + } + + 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) + fatal("Partition %d does not exist, cannot change it\n", one_only); + + z = &newp; + + while(1) { + + read_input(dev, interactive, z); + + printf("New situation:\n"); + out_partitions(dev, z); + + if (!partitions_ok(z) && !force) { + if(!interactive) + fatal("I don't like these partitions - nothing changed.\n" + "(If you really want this, use the --force option.)\n"); + else + printf("I don't like this - probably you should answer No\n"); + } + ask: + if (interactive) { + if (no_write) + printf("Are you satisfied with this? [ynq] "); + else + printf("Do you want to write this to disk? [ynq] "); + answer = c = getchar(); + while (c != '\n' && c != EOF) + c = getchar(); + if (c == EOF) + printf("\nsfdisk: premature end of input\n"); + if (c == EOF || answer == 'q' || answer == 'Q') { + fatal("Quitting - nothing changed\n"); + } else if (answer == 'n' || answer == 'N') { + continue; + } else if (answer == 'y' || answer == 'Y') { + 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; + + reread_disk_partition(dev, fd); + + 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 */ + sleep(3); + exit(exit_status); +} diff --git a/fdisk/sfdisk.examples b/fdisk/sfdisk.examples new file mode 100644 index 000000000..13e671d48 --- /dev/null +++ b/fdisk/sfdisk.examples @@ -0,0 +1,264 @@ +Examples of the use of sfdisk 3.0 (to partition a disk) +Input lines have fields <start>,<size>,<type>... - see sfdisk.8. +Usually no <start> is given, and input lines start with a comma. + +Before doing anything with a disk, make sure it is not in use; +unmount all its file systems, and say swapoff to its swap partitions. +(The final BLKRRPART ioctl will fail if anything else still uses +the disk, and you will have to reboot. It is easier to first make +sure that nothing uses the disk, e.g., by testing: + % umount /dev/sdb1 + % sfdisk -R /dev/sdb + BLKRRPART: Device or resource busy + * Device busy for revalidation (usage=2) + % swapoff /dev/sdb3 + % sfdisk -R /dev/sdb + * sdb: sdb1 < sdb5 sdb6 > sdb3 + % +Note that the starred messages are kernel messages, that may be +logged somewhere, or written to some other console. +In sfdisk 3.01 sfdisk automatically does this check, unless told not to.) + +1. One big partition: + sfdisk /dev/hda << EOF + ; + EOF + +(If there was garbage on the disk before, you may get error messages +like: `ERROR: sector 0 does not have an msdos signature' +and `/dev/hda: unrecognized partition'. This does not matter +if you write an entirely fresh partition table anyway.) + +The output will be: +----------------------------------------------------------------------- +Old situation: +... +New situation: +Units = cylinders of 208896 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 1023 1024- 208895+ 83 Linux native +Successfully wrote the new partition table + hda: hda1 +----------------------------------------------------------------------- +Writing and rereading the partition table takes a few seconds - +don't be alarmed if nothing happens for six seconds or so. + + +2. Three primary partitions: two of size 50MB and the rest: + sfdisk /dev/hda -uM << EOF + ,50 + ,50 + ; + EOF +----------------------------------------------------------------------- +New situation: +Units = megabytes of 1048576 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End MB #blocks Id System +/dev/hda1 0+ 50- 51- 51203+ 83 Linux native +/dev/hda2 50+ 100- 51- 51204 83 Linux native +/dev/hda3 100+ 203 104- 106488 83 Linux native +Successfully wrote the new partition table + hda: hda1 hda2 hda3 +----------------------------------------------------------------------- +/dev/hda1 is one block (in fact only half a block) shorter than +/dev/hda2 because its start had to be shifted away from zero in +order to leave room for the Master Boot Record (MBR). + + +3. A 1MB OS2 Boot Manager partition, a 50MB DOS partition, + and three extended partitions (DOS D:, Linux swap, Linux): + sfdisk /dev/hda -uM << EOF + ,1,a + ,50,6 + ,,E + ; + ,20,4 + ,16,S + ; + EOF +----------------------------------------------------------------------- + Device Boot Start End MB #blocks Id System +/dev/hda1 0+ 1- 2- 1223+ a OS/2 Boot Manager +/dev/hda2 1+ 51- 51- 51204 6 DOS 16-bit FAT >=32M +/dev/hda3 51+ 203 153- 156468 5 Extended +/dev/hda4 0 - 0 0 0 Empty +/dev/hda5 51+ 71- 21- 20603+ 4 DOS 16-bit FAT <32M +/dev/hda6 71+ 87- 17- 16523+ 82 Linux swap +/dev/hda7 87+ 203 117- 119339+ 83 Linux native +Successfully wrote the new partition table + hda: hda1 hda2 hda3 < hda5 hda6 hda7 > +----------------------------------------------------------------------- +All these rounded numbers look better in cylinder units: + % sfdisk -l /dev/hda +----------------------------------------------------------------------- + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 5 6- 1223+ a OS/2 Boot Manager +/dev/hda2 6 256 251 51204 6 DOS 16-bit FAT >=32M +/dev/hda3 257 1023 767 156468 5 Extended +/dev/hda4 0 - 0 0 0 Empty +/dev/hda5 257+ 357 101- 20603+ 4 DOS 16-bit FAT <32M +/dev/hda6 358+ 438 81- 16523+ 82 Linux swap +/dev/hda7 439+ 1023 585- 119339+ 83 Linux native +----------------------------------------------------------------------- +But still - why does /dev/hda5 not start on a cylinder boundary? +Because it is contained in an extended partition that does. +Of the chain of extended partitions, usually only the first is +shown. (The others have no name under Linux anyway.) But +these additional extended partitions can be made visible: + % sfdisk -l -x /dev/hda +----------------------------------------------------------------------- + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 5 6- 1223+ a OS/2 Boot Manager +/dev/hda2 6 256 251 51204 6 DOS 16-bit FAT >=32M +/dev/hda3 257 1023 767 156468 5 Extended +/dev/hda4 0 - 0 0 0 Empty + +/dev/hda5 257+ 357 101- 20603+ 4 DOS 16-bit FAT <32M + - 358 1023 666 135864 5 Extended + - 257 256 0 0 0 Empty + - 257 256 0 0 0 Empty + +/dev/hda6 358+ 438 81- 16523+ 82 Linux swap + - 439 1023 585 119340 5 Extended + - 358 357 0 0 0 Empty + - 358 357 0 0 0 Empty + +/dev/hda7 439+ 1023 585- 119339+ 83 Linux native + - 439 438 0 0 0 Empty + - 439 438 0 0 0 Empty + - 439 438 0 0 0 Empty +----------------------------------------------------------------------- + +Why the empty 4th input line? The description of the extended partitions +starts after that of the four primary partitions. +You force an empty partition with a ",0" input line, but here all +space was divided already, so the fourth partition became empty +automatically. + +How did I know about 4,6,a,E,S? Well, E,S,L stand for Extended, +Swap and Linux. The other values are hexadecimal and come from +the table: + % sfdisk -T + Id Name + + 0 Empty + 1 DOS 12-bit FAT + 2 XENIX root + 3 XENIX usr + 4 DOS 16-bit FAT <32M + 5 Extended + 6 DOS 16-bit FAT >=32M + 7 OS/2 HPFS or QNX or Advanced UNIX + 8 AIX data + 9 AIX boot or Coherent + a OS/2 Boot Manager + ... + + +4. Preserving the sectors changed by sfdisk. + % sfdisk -O save-hdd-partition-sectors /dev/hda + ... + will write the sectors overwritten by sfdisk to file. + If you notice that you trashed some partition, you may + be able to restore things by + % sfdisk -I save-hdd-partition-sectors /dev/hda + % + +5. Preserving some old partitions. + % sfdisk -N2 /dev/hda + ... + will only change the partition /dev/hda2, and leave the rest + unchanged. The most obvious application is to change an Id: + % sfdisk -N7 /dev/hda + ,,63 + % +----------------------------------------------------------------------- +Old situation: + + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 5 6- 1223+ a OS/2 Boot Manager +... +/dev/hda6 358+ 438 81- 16523+ 82 Linux swap +/dev/hda7 439+ 1023 585- 119339+ 83 Linux native + +New situation: + + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 5 6- 1223+ a OS/2 Boot Manager +... +/dev/hda6 358+ 438 81- 16523+ 82 Linux swap +/dev/hda7 439+ 1023 585- 119339+ 63 GNU HURD +----------------------------------------------------------------------- + Note that changing a logical partition into an empty partition + will decrease the number of all subsequent logical partitions. + +6. Deleting a partition. +At first I thought of having an option -X# for deleting partitions, +but there are several ways in which a partition can be deleted, and +it is probably better to handle this just as a general change. + % sfdisk -d /dev/hda > ohda +will write the current tables on the file `ohda'. +----------------------------------------------------------------------- +% cat ohda +# partition table of /dev/hda +unit: sectors + +/dev/hda1 : start= 1, size= 40799, Id= 5 +/dev/hda2 : start= 40800, size= 40800, Id=83 +/dev/hda3 : start= 81600, size= 336192, Id=83 +/dev/hda4 : start= 0, size= 0, Id= 0 +/dev/hda5 : start= 2, size= 40798, Id=83 +----------------------------------------------------------------------- +In order to delete the partition on /dev/hda3, edit this file +and feed the result to sfdisk again. +----------------------------------------------------------------------- +% emacs ohda +% cat ohda +# partition table of /dev/hda +unit: sectors + +/dev/hda1 : start= 1, size= 40799, Id= 5 +/dev/hda2 : start= 40800, size= 40800, Id=83 +/dev/hda3 : start= 0, size= 0, Id= 0 +/dev/hda4 : start= 0, size= 0, Id= 0 +/dev/hda5 : start= 2, size= 40798, Id=83 +% sfdisk /dev/hda < ohda +Old situation: +Units = cylinders of 208896 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 99 100- 20399+ 5 Extended +/dev/hda2 100 199 100 20400 83 Linux native +/dev/hda3 200 1023 824 168096 83 Linux native +/dev/hda4 0 - 0 0 0 Empty +/dev/hda5 0+ 99 100- 20399 83 Linux native +New situation: +Units = sectors of 512 bytes, counting from 0 + + Device Boot Start End #sectors Id System +/dev/hda1 1 40799 40799 5 Extended +/dev/hda2 40800 81599 40800 83 Linux native +/dev/hda3 0 - 0 0 Empty +/dev/hda4 0 - 0 0 Empty +/dev/hda5 2 40799 40798 83 Linux native +Successfully wrote the new partition table +% sfdisk -l -V /dev/hda + +Disk /dev/hda: 12 heads, 34 sectors, 1024 cylinders +Units = cylinders of 208896 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End #cyls #blocks Id System +/dev/hda1 0+ 99 100- 20399+ 5 Extended +/dev/hda2 100 199 100 20400 83 Linux native +/dev/hda3 0 - 0 0 0 Empty +/dev/hda4 0 - 0 0 0 Empty +/dev/hda5 0+ 99 100- 20399 83 Linux native +/dev/hda: OK +----------------------------------------------------------------------- +This is a good way of making changes: dump the current status +to file, edit the file, and feed it to sfdisk. +Preserving the file on some other disk could be useful: +if ever the MBR gets thrashed it can be used to restore +the old situation. |