aboutsummaryrefslogtreecommitdiffstats
path: root/disk-utils
diff options
context:
space:
mode:
Diffstat (limited to 'disk-utils')
-rw-r--r--disk-utils/Makefile57
-rw-r--r--disk-utils/README.bootutils-0.1104
-rw-r--r--disk-utils/README.cfdisk45
-rw-r--r--disk-utils/README.fdisk577
-rw-r--r--disk-utils/cfdisk.8407
-rw-r--r--disk-utils/cfdisk.c2330
-rw-r--r--disk-utils/fdformat.859
-rw-r--r--disk-utils/fdformat.c109
-rw-r--r--disk-utils/fdisk.8166
-rw-r--r--disk-utils/fdisk.c1339
-rw-r--r--disk-utils/fdprm26
-rw-r--r--disk-utils/frag.847
-rw-r--r--disk-utils/frag.c311
-rw-r--r--disk-utils/fsck.minix.8125
-rw-r--r--disk-utils/fsck.minix.c862
-rw-r--r--disk-utils/llseek.c84
-rw-r--r--disk-utils/mkfs.8133
-rw-r--r--disk-utils/mkfs.c297
-rw-r--r--disk-utils/mkfs.minix.888
-rw-r--r--disk-utils/mkfs.minix.c533
-rw-r--r--disk-utils/mkswap.886
-rw-r--r--disk-utils/mkswap.c212
-rw-r--r--disk-utils/setfdprm.866
-rw-r--r--disk-utils/setfdprm.c149
24 files changed, 8212 insertions, 0 deletions
diff --git a/disk-utils/Makefile b/disk-utils/Makefile
new file mode 100644
index 0000000000..854e1ec9d8
--- /dev/null
+++ b/disk-utils/Makefile
@@ -0,0 +1,57 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Wed Feb 22 16:09:35 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN8= cfdisk.8 fdformat.8 fdisk.8 frag.8 fsck.minix.8 \
+ mkfs.8 mkfs.minix.8 mkswap.8 setfdprm.8
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+SBIN= cfdisk fdisk fsck.minix mkfs mkfs.minix mkswap
+
+USRSBIN= frag
+
+USRBIN= fdformat setfdprm
+
+# Where to put datebase files?
+
+ETC= fdprm
+
+all: $(SBIN) $(USRSBIN) $(USRBIN)
+
+cfdisk: cfdisk.c llseek.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $< llseek.o -o $@ -lcurses -ltermcap -lm
+
+%.o: %.c
+ $(CC) -c $(CFLAGS) $< -o $@
+
+# Rules for everything else
+
+fdformat: fdformat.o
+fdisk: fdisk.o llseek.o
+frag: frag.o
+fsck.minix: fsck.minix.o
+mkfs: mkfs.o
+mkfs.minix: mkfs.minix.o
+mkswap: mkswap.o
+setfdprm: setfdprm.o
+
+install: all
+ $(INSTALLDIR) $(SBINDIR) $(USRSBINDIR) $(ETCDIR)
+ $(INSTALLBIN) $(SBIN) $(SBINDIR)
+ $(INSTALLBIN) $(USRSBIN) $(USRSBINDIR)
+ $(INSTALLBIN) $(USRBIN) $(USRBINDIR)
+ $(INSTALLDAT) $(ETC) $(ETCDIR)
+ $(INSTALLDIR) $(MAN8DIR)
+ $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+
+.PHONY: clean
+clean:
+ -rm -f *.o *~ core $(SBIN) $(USRSBIN) $(USRBIN)
diff --git a/disk-utils/README.bootutils-0.1 b/disk-utils/README.bootutils-0.1
new file mode 100644
index 0000000000..d87437e891
--- /dev/null
+++ b/disk-utils/README.bootutils-0.1
@@ -0,0 +1,104 @@
+bootutils-0.1
+
+* ALPHA RELEASE: Use at your own risk! *
+
+* You MUST have 0.99pl10 or later kernel to make use of all of the
+ facilities of this package. If you can live without the unmount-root
+ feature, then 0.99pl9 will work. *
+
+This is the first release of a set of utilities designed to automate
+the management and checking of filesystems at boot time and shutdown.
+It supports automatic and safe 'fsck' of all filesystems (including
+root) at boot time by booting with root readonly; if the fsck succeeds
+then root is remounted read-write and booting can continue.
+
+Why bother?
+
+Well, many people like to have a safe and reliable check of all their
+filesystems during boot. This is especially true for ext2fs, because
+all ext2fs filesystems have a special 'clean' flag which gets set when
+the filesystem is cleaned (by e2fsck) or is unmounted cleanly, and
+which gets unset when the filesystem is active. e2fsck can sense this
+flag, and will skip over filesystems which are clean.
+
+This means that e2fsck won't bother you with a laborious filesystem
+check at each startup, as long as you always shut down cleanly; but it
+will check your filesystems automatically if you ever have a crash,
+because afterwards the filesystem 'clean' flags will not be set. You
+*can* still mount an unclean filesystem, but ext2fs will give you a
+warning and will not mark it clean when it gets unmounted.
+
+One of the problems with automatic fsck'ing is that it is unsafe to
+check mounted, active filesystems. The solution is to initially mount
+only the root filesystem, and to mount it in readonly mode. In this
+situation, fsck can run safely on all filesystems, without the danger
+that the kernel might start conflicting with the repairs being done to
+the filesystem.
+
+If any repairs were done, it is unsafe to proceed any further because
+the kernel might have cached old information about the filesystems
+which has been updated by fsck. However, if the fsck succeeded, then
+we can remount the root filesystem in read-write mode and proceed to
+mount all of the other filesystems.
+
+Finally, in order to ensure that filesystems are correctly tidied up
+on shutdown, we need to unmount the root at shutdown. This is usually
+done automatically; the standard Linux shutdown programs do a 'umount
+- -a' command to unmount all mounted filesystems. You MUST have a
+0.99pl10 or later kernel for this to work. Many versions of umount
+explicitly do not try to unmount the root, since pre-99pl10 kernels
+forbade this. The umount included here will unmount even the root
+filesystem. (A special kernel trick in pl10 allows this to work by
+keeping the filesystem alive in readonly mode after it has been tidied
+up.)
+
+The bootup operation of this package is invoked by the /etc/rc shell
+script, an example of which is in mount/etc/rc. It contains the
+following important lines:
+
+ # Check the integrity of all filesystems
+ /bin/fsck -A -a
+ # If there was a failure, drop into single-user mode.
+ if [ $? -gt 1 ] ; then
+ echo fsck failed. Please reboot.
+ sh
+ fi
+
+ # Remount the root filesystem in read-write mode
+ /etc/mount -n -o remount /dev/hda3 /
+
+ # remove /etc/mtab* so that mount will create it with a root entry
+ /bin/rm -f /etc/mtab* /etc/nologin /etc/utmp
+
+ # mount file systems in fstab (and create an entry for /)
+ # but not NFS because TCP/IP is not yet configured
+ /etc/mount -avt nonfs
+
+
+This is the first attempt at a complete package for automated clean
+fsck support, so you may well find that you would like a slightly
+different behaviour. Please feel free to send me comments, bug
+reports and improvements!
+
+
+This package includes three separate items, shamelessly adapted from
+other, more or less standard Linux programs.
+
+* rdev.c: a modified rdev which is extended to allow the
+ readonly/readwrite status of the kernel image to be altered. Use
+ rdev -R <kernel> 1
+ to make the kernel mount in readonly mode. This can be overridden
+ by the use of the 'read-only' or 'read-write' keywords of the most
+ recent version of LILO.
+
+* Mount/umount package: This was recently posted to the net, and
+ implements the '-o remount' mount option which allows filesystems to
+ be remounted. Unlike the previous post, the version included here
+ also attempts to unmount the root filesystem on 'umount -a'. I have
+ also tried to clean up the man-pages.
+
+* fsck package: David Engel's fsck front-end. Read the README for it.
+ This package implements the 'fsck -A' command which will check all
+ filesystems in /etc/fstab automatically.
+
+Stephen Tweedie <sct@dcs.ed.ac.uk>
diff --git a/disk-utils/README.cfdisk b/disk-utils/README.cfdisk
new file mode 100644
index 0000000000..5241ad1369
--- /dev/null
+++ b/disk-utils/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/disk-utils/README.fdisk b/disk-utils/README.fdisk
new file mode 100644
index 0000000000..9e64508ed5
--- /dev/null
+++ b/disk-utils/README.fdisk
@@ -0,0 +1,577 @@
+`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 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* give a Verify command
+ 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. 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. 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 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, the prompt is simply repeated.
+
+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 do 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.
diff --git a/disk-utils/cfdisk.8 b/disk-utils/cfdisk.8
new file mode 100644
index 0000000000..cb23149fb0
--- /dev/null
+++ b/disk-utils/cfdisk.8
@@ -0,0 +1,407 @@
+.\" 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 "25 April 1994" "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.
+.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.
+
+.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 resutls, 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 "SEE ALSO"
+fdisk(8)
+.SH BUGS
+The current version does not support multiple disks (future addition).
+.SH AUTHOR
+Kevin E. Martin (martin@cs.unc.edu)
diff --git a/disk-utils/cfdisk.c b/disk-utils/cfdisk.c
new file mode 100644
index 0000000000..6c42f9c06a
--- /dev/null
+++ b/disk-utils/cfdisk.c
@@ -0,0 +1,2330 @@
+/****************************************************************************
+ *
+ * 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>
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <curses.h>
+#include <signal.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <linux/genhd.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.8a BETA (>2GB)"
+
+#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_FLAG 0xAA55
+
+#define UNUSABLE -1
+#define FREE_SPACE 0x00
+#define EXTENDED 0x05
+#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 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 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 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 short flag;
+ } 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 */
+} partition_info;
+
+char *disk_device = DEFAULT_DEVICE;
+int fd;
+int heads = 0;
+int sectors = 0;
+int cylinders = 0;
+int changed = FALSE;
+int opened = FALSE;
+
+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 = 30;
+int FSTYPE_START = 45;
+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",
+ [EXTENDED] = "Extended",
+ [0x01] = "DOS 12-bit FAT",
+ [0x04] = "DOS 16-bit < 32Mb",
+ [0x06] = "DOS 16-bit >=32Mb",
+ [0x07] = "OS/2 HPFS",
+ [0x0A] = "OS/2 Boot Manager",
+ [0xA5] = "BSD/386",
+
+/* The rest of these are taken from A. V. Le Blanc's (LeBlanc@mcc.ac.uk)
+ * fdisk program. I do not know where they came from, but I include
+ * them for completeness.
+ */
+
+ [0x02] = "XENIX root",
+ [0x03] = "XENIX usr",
+ [0x08] = "AIX",
+ [0x09] = "AIX bootable",
+ [0x40] = "Venix 80286",
+ [0x51] = "Novell?",
+ [0x52] = "Microport",
+ [0x63] = "GNU HURD",
+ [0x64] = "Novell",
+ [0x75] = "PC/IX",
+ [0x80] = "Old MINIX",
+ [0x93] = "Amoeba",
+ [0x94] = "Amoeba BBT",
+ [0xB7] = "BSDI fs",
+ [0xB8] = "BSDI swap",
+ [0xC7] = "Syrinx",
+ [0xDB] = "CP/M",
+ [0xE1] = "DOS access",
+ [0xE3] = "DOS R/O",
+ [0xF2] = "DOS secondary",
+ [0xFF] = "BBT"
+};
+
+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 (!warning_last_time)
+ return;
+
+ move(WARNING_START,0);
+ for (i = 0; i < COLS; i++)
+ addch(' ');
+
+ warning_last_time = FALSE;
+}
+
+void print_warning(char *s)
+{
+ mvaddstr(WARNING_START, (COLS-strlen(s))/2, s);
+ putchar(BELL); /* CTRL-G */
+
+ warning_last_time = TRUE;
+}
+
+void fatal(char *s)
+{
+ char str[LINE_LENGTH];
+
+ 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();
+
+ signal(SIGINT, old_SIGINT);
+ signal(SIGTERM, old_SIGTERM);
+ mvcur(0, COLS-1, LINES-1, 0);
+ nl();
+ endwin();
+ fdexit(1);
+}
+
+void read_sector(char *buffer, int sect_num)
+{
+ if (ext2_llseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
+ fatal(BAD_SEEK);
+ if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
+ fatal(BAD_READ);
+}
+
+void write_sector(char *buffer, int sect_num)
+{
+ if (ext2_llseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
+ fatal(BAD_SEEK);
+ if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
+ fatal(BAD_WRITE);
+}
+
+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 (ext_info.id == EXTENDED)
+ 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 (ext_info.id == EXTENDED)
+ 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 (ext_info.id != EXTENDED) */
+ 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 (ext_info.id == EXTENDED)
+ 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 (ext_info.id != EXTENDED) */
+ 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_part(int i, int num, int id, int flags, int first, int last,
+ int offset)
+{
+ 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 = offset;
+ p_info[i].flags = flags;
+ p_info[i].id = id;
+ p_info[i].num = num;
+
+ 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;
+ 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 i, pri = 0, log = 0;
+
+ if (num_parts == MAXIMUM_PARTS ||
+ first < 0 ||
+ first >= cylinders*heads*sectors ||
+ last < 0 ||
+ last >= cylinders*heads*sectors)
+ return -1;
+
+ for (i = 0; i < num_parts; i++)
+ if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
+ pri++;
+ else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
+ log++;
+ if (ext_info.id == EXTENDED && log > 0)
+ pri++;
+
+ if (IS_PRIMARY(num))
+ if (pri >= 4)
+ return -1;
+ else
+ pri++;
+
+ for (i = 0; p_info[i].last_sector < first; i++);
+
+ if (p_info[i].id != FREE_SPACE || last > p_info[i].last_sector)
+ return -1;
+
+ if (id == EXTENDED)
+ if (ext_info.id != FREE_SPACE)
+ return -1;
+ 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 = EXTENDED;
+ ext_info.num = num;
+
+ return 0;
+ } else
+ return -1;
+
+ if (IS_LOGICAL(num)) {
+ if (ext_info.id != EXTENDED) {
+ 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_part(i, PRI_OR_LOG, FREE_SPACE, 0,
+ p_info[i].first_sector, first-1, 0);
+ i++;
+ }
+
+ if (last != p_info[i].last_sector)
+ insert_part(i+1, PRI_OR_LOG, FREE_SPACE, 0,
+ last+1, p_info[i].last_sector, 0);
+
+ 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;
+
+ 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) ||
+ (ext_info.id == EXTENDED && 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
+{
+ char 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) && ext_info.id != EXTENDED) {
+ /* 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, EXTENDED, 0, first, last,
+ (first == 0 ? sectors : 0));
+ }
+
+ 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);
+}
+
+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 p, i;
+ 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)
+ fatal(BAD_OPEN);
+ read_sector(buffer.c.b, 0);
+
+ 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);
+
+ clear_p_info();
+
+ if (!zero_table) {
+ for (i = 0; i < 4; i++) {
+ if (buffer.p.part[i].sys_ind > 0 &&
+ add_part(i,
+ buffer.p.part[i].sys_ind,
+ buffer.p.part[i].boot_ind,
+ ((buffer.p.part[i].start_sect <= sectors) ?
+ 0 : buffer.p.part[i].start_sect),
+ buffer.p.part[i].start_sect +
+ buffer.p.part[i].nr_sects - 1,
+ ((buffer.p.part[i].start_sect <= sectors) ?
+ buffer.p.part[i].start_sect : 0))) {
+ fatal(BAD_PRIMARY);
+ }
+ if (buffer.p.part[i].sys_ind == EXTENDED)
+ tmp_ext = ext_info;
+ }
+
+ if (tmp_ext.id == EXTENDED) {
+ ext_info = tmp_ext;
+ logical_sectors[logical] = ext_info.first_sector;
+ read_sector(buffer.c.b, logical_sectors[logical++]);
+ i = 4;
+ do {
+ for (p = 0;
+ p < 4 && (!buffer.p.part[p].sys_ind ||
+ buffer.p.part[p].sys_ind == 5);
+ p++);
+ if (p > 3)
+ fatal(BAD_LOGICAL);
+
+ if (add_part(i++,
+ buffer.p.part[p].sys_ind,
+ buffer.p.part[p].boot_ind,
+ logical_sectors[logical-1],
+ logical_sectors[logical-1] +
+ buffer.p.part[p].start_sect +
+ buffer.p.part[p].nr_sects - 1,
+ buffer.p.part[p].start_sect)) {
+ fatal(BAD_LOGICAL);
+ }
+
+ for (p = 0;
+ p < 4 && buffer.p.part[p].sys_ind != 5;
+ p++);
+ if (p < 4) {
+ logical_sectors[logical] =
+ ext_info.first_sector + buffer.p.part[p].start_sect;
+ read_sector(buffer.c.b, logical_sectors[logical++]);
+ }
+ } while (p < 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))
+ p->start_sect = pi->offset;
+ else
+ p->start_sect = pi->first_sector + pi->offset;
+ p->nr_sects = 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 (ext_info.id == EXTENDED)
+ fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info);
+
+ buffer->p.flag = PART_TABLE_FLAG;
+}
+
+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.flag != (unsigned short)PART_TABLE_FLAG)
+ 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 = 5;
+ p->start_sect = pi->first_sector - ext_info.first_sector;
+ p->nr_sects = 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.flag = PART_TABLE_FLAG;
+}
+
+void write_part_table(void)
+{
+ int i, done = FALSE, len;
+ partition_table buffer;
+ char response[LINE_LENGTH];
+
+ 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 the extended partition table from disk ??? KEM */
+ 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);
+ }
+
+ 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);
+}
+
+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);
+
+ 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)) {
+ 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[21];
+
+ 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, "%7d%c", p->first_sector,
+ ((p->first_sector/(sectors*heads)) !=
+ ((float)p->first_sector/(sectors*heads)) ?
+ '*' : ' '));
+
+ fp_printf(fp, " ");
+
+ fp_printf(fp, "%7d%c", p->last_sector,
+ (((p->last_sector+1)/(sectors*heads)) !=
+ ((float)(p->last_sector+1)/(sectors*heads)) ?
+ '*' : ' '));
+
+ fp_printf(fp, " ");
+
+ fp_printf(fp, "%6d%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)) ?
+ '#' : ' '));
+
+ fp_printf(fp, " ");
+
+ size = p->last_sector - p->first_sector + 1;
+ fp_printf(fp, "%7d%c", size,
+ ((size/(sectors*heads)) != ((float)size/(sectors*heads)) ?
+ '*' : ' '));
+
+ fp_printf(fp, " ");
+
+ if (p->id == UNUSABLE)
+ sprintf(part_str, "%.16s", "Unusable");
+ else if (p->id == FREE_SPACE)
+ sprintf(part_str, "%.16s", "Free Space");
+ else if (partition_type[p->id])
+ sprintf(part_str, "%.16s (%02X)", partition_type[p->id], p->id);
+ else
+ sprintf(part_str, "%.16s (%02X)", "Unknown", p->id);
+ fp_printf(fp, "%-21.21s", 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 = (ext_info.id == EXTENDED);
+
+ 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 %7d %7d\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\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 (ext_info.id == EXTENDED && 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 Kevin E. Martin",
+ "",
+ "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) {
+ if (p_info[num_parts-1].last_sector > heads*sectors*cylinders-1) {
+ while (p_info[num_parts-1].first_sector > heads*sectors*cylinders-1) {
+ 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 = heads*sectors*cylinders - 1;
+
+ if (ext_info.last_sector > heads*sectors*cylinders-1)
+ ext_info.last_sector = heads*sectors*cylinders - 1;
+ } else if (p_info[num_parts-1].last_sector < heads*sectors*cylinders-1) {
+ if (p_info[num_parts-1].id == FREE_SPACE ||
+ p_info[num_parts-1].id == UNUSABLE) {
+ p_info[num_parts-1].last_sector = heads*sectors*cylinders - 1;
+ } else {
+ insert_part(num_parts, PRI_OR_LOG, FREE_SPACE, 0,
+ p_info[num_parts-1].last_sector+1,
+ heads*sectors*cylinders-1, 0);
+ }
+ }
+
+ /* 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 = 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 (new_id == EXTENDED)
+ 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;
+
+ 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", 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"))))));
+ if (p_info[i].id == UNUSABLE)
+ mvaddstr(y, FSTYPE_START, "Unusable");
+ else if (p_info[i].id == FREE_SPACE)
+ mvaddstr(y, FSTYPE_START, "Free Space");
+ else if (partition_type[p_info[i].id])
+ mvaddstr(y, FSTYPE_START, partition_type[p_info[i].id]);
+ else
+ mvprintw(y, FSTYPE_START, "Unknown (%02X)", p_info[i].id);
+
+ 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;
+ 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, "Part Type");
+ mvaddstr(DISK_TABLE_START, FSTYPE_START, "FS Type");
+ 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 die(int dummy)
+{
+ signal(SIGINT, old_SIGINT);
+ signal(SIGTERM, old_SIGTERM);
+ mvcur(0, COLS-1, LINES-1, 0);
+ nl();
+ endwin();
+ fdexit(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 }
+ };
+
+ initscr();
+ old_SIGINT = signal(SIGINT, die);
+ old_SIGTERM = signal(SIGTERM, die);
+#ifdef DEBUG
+ signal(SIGINT, old_SIGINT);
+ signal(SIGTERM, old_SIGTERM);
+#endif
+
+ cbreak();
+ noecho();
+ nonl();
+
+ init_const();
+
+ fill_p_info();
+
+ draw_screen();
+
+ while (!done) {
+ (void)draw_cursor(0);
+
+ if (p_info[cur_part].id == FREE_SPACE)
+ command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "hnpquW",
+ MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
+ else if (p_info[cur_part].id > 0)
+ command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "bdhmpqtuW",
+ MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
+ else
+ command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "hpquW",
+ 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 */
+ }
+ }
+
+ signal(SIGINT, old_SIGINT);
+ signal(SIGTERM, old_SIGTERM);
+ mvcur(0, COLS-1, LINES-1, 0);
+ nl();
+ endwin();
+ fdexit(0);
+}
+
+void copyright(void)
+{
+ fprintf(stderr, "Copyright (C) 1994 Kevin E. Martin\n");
+}
+
+void usage(char *prog_name)
+{
+ fprintf(stderr,
+ "%s: [-avz] [-c # cylinders] [-h # heads] [-s # sectors/track]\n",
+ prog_name);
+ fprintf(stderr,
+ "[ -P opt ] device\n");
+ copyright();
+}
+
+void main(int argc, char **argv)
+{
+ char c;
+ int i, len;
+
+ 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_RDWR)) < 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();
+}
diff --git a/disk-utils/fdformat.8 b/disk-utils/fdformat.8
new file mode 100644
index 0000000000..9e24b0ae4b
--- /dev/null
+++ b/disk-utils/fdformat.8
@@ -0,0 +1,59 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FDFORMAT 8 "1 February 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+fdformat \- Low-level formats a floppy disk
+.SH SYNOPSIS
+.B fdformat
+.B "[ \-n ]"
+device
+.SH DESCRIPTION
+.B fdformat
+does a low level format on a floppy disk.
+.I device
+is usually one of the following (for floppy devices, the major = 2, and the
+minor is shown for informational purposes only):
+.sp
+.nf
+.RS
+/dev/fd0d360 (minor = 4)
+/dev/fd0h1200 (minor = 8)
+/dev/fd0D360 (minor = 12)
+/dev/fd0H360 (minor = 12)
+/dev/fd0D720 (minor = 16)
+/dev/fd0H720 (minor = 16)
+/dev/fd0h360 (minor = 20)
+/dev/fd0h720 (minor = 24)
+/dev/fd0H1440 (minor = 28)
+
+/dev/fd1d360 (minor = 5)
+/dev/fd1h1200 (minor = 9)
+/dev/fd1D360 (minor = 13)
+/dev/fd1H360 (minor = 13)
+/dev/fd1D720 (minor = 17)
+/dev/fd1H720 (minor = 17)
+/dev/fd1h360 (minor = 21)
+/dev/fd1h720 (minor = 25)
+/dev/fd1H1440 (minor = 29)
+.RE
+.fi
+
+The generic floppy devices, /dev/fd0 and /dev/fd1, will fail to work with
+.B fdformat
+when a non-standard format is being used, or if the format has not been
+autodetected earlier. In this case, use
+.BR setfdprm (8)
+to load the disk parameters.
+
+.SH OPTIONS
+.TP
+.B \-n
+No verify. This option will disable the verification that is performed
+after the format.
+.SH "SEE ALSO"
+.BR fd (4),
+.BR setfdprm (8),
+.BR mkfs (8),
+.BR emkfs (8)
+.SH AUTHOR
+Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
diff --git a/disk-utils/fdformat.c b/disk-utils/fdformat.c
new file mode 100644
index 0000000000..7fb78af6a8
--- /dev/null
+++ b/disk-utils/fdformat.c
@@ -0,0 +1,109 @@
+/* fdformat.c - Low-level formats a floppy disk. */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <linux/fd.h>
+#include <linux/fs.h>
+
+static int ctrl;
+struct floppy_struct param;
+
+#define FLOPPY_MAJOR 2
+#define SECTOR_SIZE 512
+#define PERROR(msg) { perror(msg); exit(1); }
+
+static void format_disk(char *name)
+{
+ struct format_descr descr;
+ int track;
+
+ printf("Formatting ... ");
+ fflush(stdout);
+ if (ioctl(ctrl,FDFMTBEG,NULL) < 0) PERROR("\nioctl(FDFMTBEG)");
+ for (track = 0; track < param.track; track++) {
+ descr.track = track;
+ descr.head = 0;
+ if (ioctl(ctrl,FDFMTTRK,(int) &descr) < 0) PERROR("\nioctl(FDFMTTRK)");
+ printf("%3d\b\b\b",track);
+ fflush(stdout);
+ if (param.head == 2) {
+ descr.head = 1;
+ if (ioctl(ctrl,FDFMTTRK,(int) &descr) < 0)
+ PERROR("\nioctl(FDFMTTRK)");
+ }
+ }
+ if (ioctl(ctrl,FDFMTEND,NULL) < 0) PERROR("\nioctl(FDFMTEND)");
+ printf("done\n");
+}
+
+
+static void verify_disk(char *name)
+{
+ unsigned char *data;
+ int fd,cyl_size,cyl,count;
+
+ cyl_size = param.sect*param.head*512;
+ if ((data = (unsigned char *) malloc(cyl_size)) == NULL) PERROR("malloc");
+ printf("Verifying ... ");
+ fflush(stdout);
+ if ((fd = open(name,O_RDONLY)) < 0) PERROR(name);
+ for (cyl = 0; cyl < param.track; cyl++) {
+ printf("%3d\b\b\b",cyl);
+ fflush(stdout);
+ if (read(fd,data,cyl_size) != cyl_size) PERROR("read");
+ for (count = 0; count < cyl_size; count++)
+ if (data[count] != FD_FILL_BYTE) {
+ printf("bad data in cyl %d\nContinuing ... ",cyl);
+ fflush(stdout);
+ break;
+ }
+ }
+ printf("done\n");
+ if (close(fd) < 0) PERROR("close");
+}
+
+
+static void usage(char *name)
+{
+ char *this;
+
+ if (this = strrchr(name,'/')) name = this+1;
+ fprintf(stderr,"usage: %s [ -n ] device\n",name);
+ exit(1);
+}
+
+
+int main(int argc,char **argv)
+{
+ int verify;
+ char *name;
+ struct stat st;
+
+ name = argv[0];
+ verify = 1;
+ if (argc > 1 && argv[1][0] == '-') {
+ if (argv[1][1] != 'n') usage(name);
+ verify = 0;
+ argc--;
+ argv++;
+ }
+ if (argc != 2) usage(name);
+ if (lstat(argv[1],&st) < 0) PERROR(argv[1]);
+ if (!S_ISBLK(st.st_mode) || MAJOR(st.st_rdev) != FLOPPY_MAJOR) {
+ fprintf(stderr,"%s: not a floppy device\n",argv[1]);
+ exit(1);
+ }
+ if (access(argv[1],W_OK) < 0) PERROR(argv[1]);
+ if ((ctrl = open(argv[1],3)) < 0) PERROR(argv[1]);
+ if (ioctl(ctrl,FDGETPRM,(int) &param) < 0) PERROR("ioctl(FDGETPRM)");
+ printf("%sle-sided, %d tracks, %d sec/track. Total capacity %d kB.\n",
+ param.head ? "Doub" : "Sing",param.track,param.sect,param.size >> 1);
+ format_disk(argv[1]);
+ if (verify) verify_disk(argv[1]);
+ return 0;
+}
diff --git a/disk-utils/fdisk.8 b/disk-utils/fdisk.8
new file mode 100644
index 0000000000..d1891bb245
--- /dev/null
+++ b/disk-utils/fdisk.8
@@ -0,0 +1,166 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FDISK 8 "Tue Mar 22 01:00:00 1994" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+fdisk \- Partition table manipulator for Linux
+.SH SYNOPSIS
+.B fdisk
+.B "[ \-l ] [ \-v ] [ \-s partition] ["
+device
+.B ]
+.SH DESCRIPTION
+.B fdisk
+is a menu driven program for manipulation of the hard disk partition table.
+The
+.I device
+is usually one of the following:
+.sp
+.nf
+.RS
+/dev/hda
+/dev/hdb
+/dev/sda
+/dev/sdb
+.RE
+.fi
+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 hard disk in the system.
+
+If possible,
+.B fdisk
+will obtain the disk geometry automatically. This is
+.I not
+necessarily the
+.I physical
+disk geometry, but is the disk geometry that MS-DOS uses for the partition
+table. If
+.B fdisk
+warns you that you need to set the disk geometry, please believe this
+statement, and set the geometry. This should only be necessary with
+certain SCSI host adapters (the drivers for which are rapidly being
+modified to provide geometry information automatically).
+
+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).
+
+Old versions of fdisk (all versions prior to 1.1r [including 0.93])
+incorrectly mapped the cylinder/head/sector specification onto absolute
+sectors. This may result in the first partition on a drive failing the
+consistency check. If you use LILO to boot, this situation can be ignored.
+However, there are reports that the OS/2 boot manager will not boot a
+partition with inconsistent data.
+
+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.
+
+In version 1.1r, a BLKRRPART ioctl() is performed before exiting when the
+partition table is updated. This is primarily to ensure that removable
+SCSI disks have their partition table information updated. If the kernel
+does not update its partition table information, fdisk warns you to
+reboot. If you do not reboot your system after receiving such a warning,
+you may lose or corrupt the data on the disk. Sometimes BLKRRPART fails
+silently, when installing Linux, you should
+.I always
+reboot after editing the partition table.
+
+.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.
+
+.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 resutls, 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
+Prints version number of
+.B fdisk
+program.
+.TP
+.B \-l
+Lists the partition tables for
+.BR /dev/hda ,
+.BR /dev/hdb ,
+.BR /dev/sda ,
+.BR /dev/sdb ,
+.BR /dev/sdc ,
+.BR /dev/sdd ,
+.BR /dev/sde ,
+.BR /dev/sdf ,
+.BR /dev/sdg ,
+.BR /dev/sdh ,
+and then exits.
+.TP
+.BI \-s partition
+If the
+.I partition
+is not a DOS partition (i.e., the partition id is greater than 10), then
+the
+.I size
+of that partition 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.
+.SH BUGS
+Although this man page (written by faith@cs.unc.edu) is poor, there is
+.I excellent
+documentation in the README.fdisk file (written by LeBlanc@mcc.ac.uk) that
+should always be with the fdisk distribution. If you cannot find this file
+in the
+.I util-linux-*
+directory or with the
+.I fdisk.c
+source file, then you should write to the distributor of your version of
+.B fdisk
+and complain that you do not have all of the available documentation.
+.SH AUTHOR
+A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+.br
+v1.0r: SCSI and extfs support added by Rik Faith (faith@cs.unc.edu)
+.br
+v1.1r: Bug fixes and enhancements by Rik Faith (faith@cs.unc.edu), with
+special thanks to Michael Bischoff (i1041905@ws.rz.tu-bs.de or
+mbi@mo.math.nat.tu-bs.de).
+.br
+v1.3: Latest enhancements and bug fixes by A. V. Le Blanc, including the
+addition of the
+.B \-s
+option.
+.bt
+v2.0: Disks larger than 2GB are now fully supported, thanks to Remy Card's
+llseek support.
diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c
new file mode 100644
index 0000000000..3c0a328d5e
--- /dev/null
+++ b/disk-utils/fdisk.c
@@ -0,0 +1,1339 @@
+/* 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)
+ */
+
+
+#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 <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/fs.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 fd,
+ ext2_loff_t offset,
+ unsigned int origin);
+
+#define hex_val(c) ({ \
+ char _c = (c); \
+ isdigit(_c) ? _c - '0' : \
+ tolower(_c) + 10 - 'a'; \
+ })
+
+
+#define VERSION "2.0a (>2GB)"
+
+#define DEFAULT_DEVICE "/dev/hda"
+#define ALTERNATE_DEVICE "/dev/sda"
+#define LINE_LENGTH 80
+#define MAXIMUM_PARTS 60
+#define SECTOR_SIZE 512
+#define PART_TABLE_FLAG 0xaa55
+#define table_check(b) ((unsigned short *)((b) + 0x1fe))
+#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 calculate(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; \
+ }
+
+#define cround(n) (((n) + display_factor * unit_flag) / display_factor)
+#define ACTIVE_FLAG 0x80
+#define EXTENDED 5
+
+#define LINUX_PARTITION 0x81
+#define LINUX_SWAP 0x82
+#define LINUX_NATIVE 0x83
+
+enum failure {usage, unable_to_open, unable_to_read, unable_to_seek,
+ unable_to_write, out_of_memory};
+
+char *disk_device = DEFAULT_DEVICE, /* hda, unless specified */
+ *line_ptr, /* interactive input */
+ line_buffer[LINE_LENGTH],
+ changed[MAXIMUM_PARTS], /* marks changed buffers */
+ buffer[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 */
+ size_flag = 0,
+ dos_compatible_flag = ~0,
+ partitions = 4; /* maximum partition + 1 */
+
+uint heads,
+ sectors,
+ cylinders,
+ sector_offset = 1,
+ display_factor = 1, /* in units/sector */
+ unit_flag = 1,
+ full_bits = 0, /* 1024 cylinders in sectors */
+ extended_offset = 0, /* offset of link pointers */
+ offsets[MAXIMUM_PARTS] = {0, 0, 0, 0};
+
+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 {
+ unsigned char index;
+ char *name;
+ } sys_types[] = {
+ {0, "Empty"},
+ {1, "DOS 12-bit FAT"},
+ {2, "XENIX root"},
+ {3, "XENIX usr"},
+ {4, "DOS 16-bit <32M"},
+ {EXTENDED, "Extended"},
+ {6, "DOS 16-bit >=32M"},
+ {7, "OS/2 HPFS"}, /* or QNX? */
+ {8, "AIX"},
+ {9, "AIX bootable"},
+ {10, "OS/2 Boot Manager"},
+ {0x40, "Venix 80286"},
+ {0x51, "Novell?"},
+ {0x52, "Microport"}, /* or CPM? */
+ {0x63, "GNU HURD"}, /* or System V/386? */
+ {0x64, "Novell"},
+ {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"},
+
+ {0x93, "Amoeba"},
+ {0x94, "Amoeba BBT"}, /* (bad block table) */
+ {0xa5, "BSD/386"},
+ {0xb7, "BSDI fs"},
+ {0xb8, "BSDI swap"},
+ {0xc7, "Syrinx"},
+ {0xdb, "CP/M"}, /* or Concurrent DOS? */
+ {0xe1, "DOS access"},
+ {0xe3, "DOS R/O"},
+ {0xf2, "DOS secondary"},
+ {0xff, "BBT"} /* (bad track table) */
+ };
+
+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 [-l] [-v] [-s /dev/hdxn] [/dev/hdx]\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)
+{
+ puts("Command action\n"
+ " a toggle a bootable flag\n"
+ " c toggle the dos compatiblity flag\n"
+ " d delete a partition\n"
+ " l list known partition types\n"
+ " m print this menu\n"
+ " n add a new partition\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)
+{
+ puts("Command action\n"
+ " b move beginning of data in a partition\n"
+ " c change number of cylinders\n"
+ " d print the raw data in the partition table\n"
+ " e list extended partitions\n"
+ " 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"
+ " w write table to disk and exit"
+ );
+}
+
+char *partition_type(unsigned char type)
+{
+ int high = sizeof(sys_types) / sizeof(struct systypes),
+ low = 0, mid;
+ uint tmp;
+
+ while (high >= low) {
+ mid = (high + low) >> 1;
+ if ((tmp = sys_types[mid].index) == type)
+ return sys_types[mid].name;
+ else if (tmp < type)
+ low = mid + 1;
+ else high = mid - 1;
+ }
+ return NULL;
+}
+
+void list_types(void)
+{
+ uint last[4], done = 0, next = 0,
+ size = sizeof(sys_types) / sizeof(struct systypes);
+ int 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_types[next].index, sys_types[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;
+ p->start_sect = 0;
+ p->nr_sects = 0;
+}
+
+void set_partition(int i, struct partition *p, uint start, uint stop,
+ int sys, uint offset)
+{
+ p->boot_ind = 0;
+ p->sys_ind = sys;
+ p->start_sect = start - offset;
+ p->nr_sects = 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;
+}
+
+uint rounded(uint calcul, uint start)
+{
+ uint i;
+ if (!full_bits)
+ return calcul;
+ while ((i = calcul + full_bits) <= start)
+ calcul = i;
+ return calcul;
+}
+
+void update_units(void)
+{
+ full_bits = 1024 * heads * sectors;
+ if (unit_flag && full_bits)
+ display_factor = full_bits >> 10;
+ else display_factor = 1;
+}
+
+void warn_cylinders(void)
+{
+ update_units();
+ if (cylinders > 1024)
+ fprintf(stderr, "The number of cylinders for this disk is "
+ "set to %d.\nThis is larger than 1024, and may cause "
+ "problems with:\n"
+ "1) software that runs at boot time (e.g., LILO)\n"
+ "2) booting and partitioning software form 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 (!p->start_sect)
+ fprintf(stderr, "Bad offset in primary extended partition\n");
+ else while (p->sys_ind == EXTENDED) {
+ 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 + p->start_sect;
+ if (!extended_offset)
+ extended_offset = p->start_sect;
+ if (ext2_llseek(fd, 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 (p->sys_ind == EXTENDED)
+ 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 get_boot(void)
+{
+ int i;
+ struct hd_geometry geometry;
+
+ partitions = 4;
+ if ((fd = open(disk_device, O_RDWR)) < 0)
+ fatal(unable_to_open);
+ if (SECTOR_SIZE != read(fd, buffer, SECTOR_SIZE))
+ fatal(unable_to_read);
+ if (!ioctl(fd, HDIO_REQ, &geometry)) {
+ heads = geometry.heads;
+ sectors = geometry.sectors;
+ cylinders = geometry.cylinders;
+ if (dos_compatible_flag)
+ sector_offset = sectors;
+ warn_cylinders();
+ }
+ else update_units();
+ warn_geometry();
+
+ for (i = 0; i < 4; i++)
+ if(part_table[i]->sys_ind == EXTENDED)
+ 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 (*table_check(buffers[i]) != PART_TABLE_FLAG) {
+ fprintf(stderr, "Warning: invalid flag %04x of parti"
+ "tion table %d will be corrected by w(rite)\n",
+ *table_check(buffers[i]), i + 1);
+ changed[i] = 1;
+ }
+}
+
+int read_line(void)
+{
+ if (!fgets(line_buffer, LINE_LENGTH, stdin))
+ return 0;
+ line_ptr = line_buffer;
+ 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;
+}
+
+uint read_int(uint low, uint high, char *mesg)
+{
+ uint i;
+ char ms[70];
+ sprintf(ms, "%s (%d-%d): ", mesg, low, high);
+
+ while (1) {
+ while (!isdigit(read_char(ms)) &&
+ (!size_flag || *line_ptr != '+'));
+ if (*line_ptr == '+') {
+ i = atoi(++line_ptr);
+ while (isdigit(*line_ptr))
+ line_ptr++;
+ switch (*line_ptr) {
+ case 'c':
+ case 'C': if (!unit_flag)
+ i *= heads * sectors;
+ break;
+ case 'k':
+ case 'K': i *= 2;
+ i /= display_factor;
+ break;
+ case 'm':
+ case 'M': i *= 2048;
+ i /= display_factor;
+ break;
+ default: break;
+ }
+ i += low;
+ }
+ else i = atoi(line_ptr);
+ if (i >= low && i <= high)
+ break;
+ }
+ size_flag = 0;
+ return i;
+}
+
+int get_partition(int warn, int max)
+{
+ int i = read_int(1, max, "Partition number") - 1;
+
+ if (warn && !part_table[i]->sys_ind)
+ 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 (p->sys_ind == EXTENDED && !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 (i < 4) {
+ if (p->sys_ind == EXTENDED && 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;
+ p->start_sect = q->start_sect;
+ p->nr_sects = q->nr_sects;
+ changed[i - 1] = 1;
+ }
+ else {
+ part_table[5]->start_sect +=
+ 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;
+ struct partition *p = part_table[i];
+
+ if ((sys = p->sys_ind) == EXTENDED)
+ printf("Partition %d is extended. Delete it\n", i + 1);
+ else if (!sys)
+ printf("Partition %d does not exist yet!\n", i + 1);
+ else while (1) {
+ read_char("Hex code (type L to list codes): ");
+ if (tolower(*line_ptr) == 'l')
+ list_types();
+ else if (isxdigit(*line_ptr)) {
+ sys = 0;
+ do
+ sys = sys << 4 | hex_val(*line_ptr++);
+ while (isxdigit(*line_ptr));
+ if (!sys) {
+ delete_partition(i);
+ break;
+ }
+ else if (sys == EXTENDED) {
+ printf("You may not change a partition "
+ "to be an extended partition\n");
+ break;
+ }
+ else if (sys < 256) {
+ if (sys == p->sys_ind)
+ break;
+ 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(p->start_sect, &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+ long2chs(p->start_sect + p->nr_sects - 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);
+ }
+
+/* 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);
+ }
+
+/* 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_table(void)
+{
+ struct partition *p;
+ char *type;
+ int i, w = strlen(disk_device);
+
+ printf("\nDisk %s: %d heads, %d sectors, %d cylinders\nUnits = "
+ "%ss of %d * 512 bytes\n\n", disk_device, heads, sectors,
+ cylinders, str_units(), display_factor);
+ if (w < 5)
+ w = 5;
+ printf("%*s Boot Begin Start End Blocks Id System\n",
+ w + 1, "Device");
+ for (i = 0 ; i < partitions; i++)
+ if ((p = part_table[i])->sys_ind) {
+ printf("%*s%-2d %c%8d%8d%8d%8d%c %2x %s\n", w,
+ disk_device, i + 1,
+ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
+ ? '*' : '?',
+ cround(rounded( calculate(p->head, p->sector, p->cyl),
+ p->start_sect + offsets[i])),
+ cround(p->start_sect + offsets[i]),
+ cround(p->start_sect + offsets[i] + p->nr_sects
+ - (p->nr_sects ? 1: 0)),
+ p->nr_sects / 2, p->nr_sects & 1 ? '+' : ' ',
+ p->sys_ind,
+ (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]) {
+ 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),
+ p->start_sect, p->nr_sects, p->sys_ind);
+ if (p->sys_ind)
+ check_consistency(p, i);
+ }
+}
+
+void check_bounds(uint *first, uint *last)
+{
+ int i;
+ uint max = 256 * 63 * 1024;
+ struct partition *p = part_table[0];
+
+ for (i = 0; i < partitions; p = part_table[++i])
+ if (!p->sys_ind || p->sys_ind == EXTENDED) {
+ first[i] = max;
+ last[i] = 0;
+ }
+ else {
+ first[i] = rounded(calculate(p->head, p->sector,
+ p->cyl), p->start_sect + offsets[i]);
+ last[i] = p->start_sect + offsets[i] + p->nr_sects - 1;
+ }
+}
+
+void check(int n, uint h, uint s, uint c, uint start)
+{
+ uint total, real_s, real_c, i;
+
+ real_s = sector(s) - 1;
+ real_c = cylinder(s, c);
+ total = (real_c * sectors + real_s) * heads + h;
+ if (full_bits)
+ while ((i = total + full_bits) <= start) {
+ real_c += 1024;
+ total = i;
+ }
+ 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 (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,
+ first[partitions], last[partitions];
+ struct partition *p = part_table[0];
+
+ if (warn_geometry())
+ return;
+
+ check_bounds(first, last);
+ for (i = 0; i < partitions; p = part_table[++i])
+ if (p->sys_ind && (p->sys_ind != EXTENDED)) {
+ check_consistency(p, i);
+ if (p->start_sect + 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 = part_table[ext_index]->start_sect +
+ part_table[ext_index]->nr_sects - 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)
+ 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;
+ }
+ check_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] = q->start_sect + q->nr_sects - 1;
+ }
+ }
+ else {
+ start = extended_offset + sector_offset;
+ limit = q->start_sect + q->nr_sects - 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++) {
+ if (start == offsets[i])
+ start += sector_offset;
+ if (start >= first[i] && start <= last[i])
+ if (n < 4)
+ start = last[i] + 1;
+ else
+ start = last[i] + sector_offset;
+ }
+ if (start > limit)
+ break;
+ if (start != temp && read) {
+ printf("Sector %d is already allocated\n", temp);
+ temp = start = stop;
+ read = 0;
+ }
+ if (!read && start == temp) {
+ uint i;
+ temp = 0;
+ start = read_int(cround(i = (stop = start) + (n > 4)),
+ cround(limit), 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());
+ size_flag = 1;
+ stop = read_int(cround(start), cround(limit), mesg);
+ if (unit_flag) {
+ stop = stop * display_factor - 1;
+ if (stop >limit)
+ stop = limit;
+ }
+ }
+
+ set_partition(n, p, start, stop, sys, offsets[n]);
+
+ if (sys == EXTENDED) {
+ 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 = p->nr_sects) & 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 (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;
+ }
+ }
+}
+
+void write_table(void)
+{
+ int i, error = 0;
+
+ changed[3] = changed[0] || changed[1] || changed[2] || changed[3];
+ for (i = 3; i < partitions; i++)
+ if (changed[i]) {
+ *table_check(buffers[i]) = PART_TABLE_FLAG;
+ if (ext2_llseek(fd, offsets[i]
+ * SECTOR_SIZE, SEEK_SET) < 0)
+ fatal(unable_to_seek);
+ if (write(fd, buffers[i], SECTOR_SIZE) != SECTOR_SIZE)
+ fatal(unable_to_write);
+ }
+
+ printf("The partition table has been altered!\n\n");
+
+ printf("Calling ioctl() to re-read partition table.\n"
+ "(Reboot to ensure the partition table has been updated.)\n");
+ sync();
+ sleep(2);
+ if (i = ioctl(fd, BLKRRPART))
+ error = errno;
+ close(fd);
+
+ printf("Syncing disks.\n");
+ sync();
+ sleep(4); /* for sync() */
+
+ if (i)
+ printf("Re-read table failed with error %d: %s.\nReboot your "
+ "system to ensure the partition table is updated.\n",
+ error, strerror(error));
+
+ 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);
+ 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 || !p->nr_sects || p->sys_ind == EXTENDED) {
+ printf("Partition %d has no data area\n", i + 1);
+ return;
+ }
+ first = rounded(calculate(p->head, p->sector, p->cyl), p->start_sect +
+ offsets[i]);
+ new = read_int(first, p->start_sect + p->nr_sects + offsets[i] - 1,
+ "New beginning of data") - offsets[i];
+
+ if (new != p->nr_sects) {
+ first = p->nr_sects + p->start_sect - new;
+ p->nr_sects = first;
+ p->start_sect = new;
+ changed[i] = 1;
+ }
+}
+
+void xselect(void)
+{
+ while(1) {
+ putchar('\n');
+ switch (tolower(read_char("Expert command (m for help): "))) {
+ case 'b': move_begin(get_partition(0, partitions));
+ break;
+ case 'c': cylinders = read_int(1, 65535,
+ "Number of cylinders");
+ warn_cylinders();
+ break;
+ case 'd': print_raw();
+ break;
+ case 'e': x_list_table(1);
+ break;
+ case 'h': heads = read_int(1, 256, "Number of heads");
+ update_units();
+ break;
+ case 'p': x_list_table(0);
+ break;
+ case 'q': close(fd);
+ exit(0);
+ case 'r': return;
+ case 's': sectors = read_int(1, 63,
+ "Number of sectors");
+ if (dos_compatible_flag) {
+ sector_offset = sectors;
+ fprintf(stderr, "Warning: setting "
+ "sector offset for DOS "
+ "compatiblity\n");
+ }
+ update_units();
+ break;
+ case 'w': write_table();
+ default: xmenu();
+ }
+ }
+}
+
+void try(char *device)
+{
+ disk_device = device;
+ if (!setjmp(listingbuf))
+ if ((fd = open(disk_device, O_RDWR)) >= 0) {
+ close(fd);
+ get_boot();
+ list_table();
+ if (partitions > 4)
+ delete_partition(ext_index);
+ }
+}
+
+void main(int argc, char **argv)
+{
+ if (argc > 3)
+ fatal(usage);
+ if (argc > 1 && *argv[1] == '-') {
+ switch (*(argv[1] + 1)) {
+ case 'v':
+ printf("fdisk v" VERSION "\n");
+ exit(0);
+ case 'l':
+ listing = 1;
+ try("/dev/hda");
+ try("/dev/hdb");
+ try("/dev/hdc");
+ try("/dev/hdd");
+ try("/dev/sda");
+ try("/dev/sdb");
+ try("/dev/sdc");
+ try("/dev/sdd");
+ try("/dev/sde");
+ try("/dev/sdf");
+ try("/dev/sdg");
+ try("/dev/sdh");
+ exit(0);
+ case 's': {
+ int i;
+ if (argc < 3)
+ fatal(usage);
+ if (!(i = atoi(argv[2] + 8)))
+ fatal(usage);
+ disk_device = (char *) malloc(9);
+ strncpy(disk_device, argv[2], 8);
+ if ((fd = open(disk_device, O_RDWR)) >= 0) {
+ close(fd);
+ get_boot();
+ if (i > partitions) exit(1);
+ if (part_table[--i]->sys_ind > 10)
+ printf("%d\n",
+ part_table[i]->nr_sects / 2);
+ else exit(1);
+ exit(0);
+ }
+ }
+ default:
+ fatal(usage);
+ }
+ }
+ if (argc > 1)
+ disk_device = argv[argc - 1];
+ else if ((fd = open(DEFAULT_DEVICE, O_RDWR)) < 0)
+ disk_device = ALTERNATE_DEVICE;
+ else close(fd);
+
+ get_boot();
+ if (argc == 1)
+ printf("Using %s as default device!\n", disk_device);
+
+ while (1) {
+ putchar('\n');
+ switch (tolower(read_char("Command (m for help): "))) {
+ case 'a': toggle_active(get_partition(1, partitions));
+ break;
+ case 'c':
+ toggle_dos();
+ break;
+ case 'd': delete_partition(
+ get_partition(1, partitions));
+ break;
+ case 'l': list_types();
+ break;
+ case 'n': new_partition();
+ break;
+ case 'p': list_table();
+ break;
+ case 'q': close(fd);
+ exit(0);
+ case 't': change_sysid();
+ break;
+ case 'u': change_units();
+ break;
+ case 'v': verify();
+ break;
+ case 'w': write_table();
+ case 'x': xselect();
+ break;
+ default: menu();
+ }
+ }
+}
diff --git a/disk-utils/fdprm b/disk-utils/fdprm
new file mode 100644
index 0000000000..2a59da0180
--- /dev/null
+++ b/disk-utils/fdprm
@@ -0,0 +1,26 @@
+# /etc/fdprm - floppy disk parameter table
+
+# Common disk formats. Names are of the form
+# actual media capacity/maximum drive capacity
+# (Note: although 5.25" HD drives can format disks at 1.44M, they're listed
+# as 1200 because that's the common maximum size.)
+
+# size sec/t hds trk stre gap rate spec1 fmt_gap
+360/360 720 9 2 40 0 0x2A 0x02 0xDF 0x50
+1200/1200 2400 15 2 80 0 0x1B 0x00 0xDF 0x54
+360/720 720 9 2 40 1 0x2A 0x02 0xDF 0x50
+720/720 1440 9 2 80 0 0x2A 0x02 0xDF 0x50
+720/1440 1440 9 2 80 0 0x2A 0x02 0xDF 0x50
+360/1200 720 9 2 40 1 0x23 0x01 0xDF 0x50
+720/1200 1440 9 2 80 0 0x23 0x01 0xDF 0x50
+1440/1440 2880 18 2 80 0 0x1B 0x00 0xCF 0x6C
+
+# Non-standard disk formats:
+
+# BEWARE: They're incomplete and possibly incorrect. The only reason why
+# they are in this file is to show how such formats are added.
+
+1440/1200 2880 18 2 80 0 ???? ???? ???? ???? # ?????
+1680/1440 3360 21 2 80 0 0x0C 0x00 0xCF 0x6C # ?????
+
+# Add user-specific formats here
diff --git a/disk-utils/frag.8 b/disk-utils/frag.8
new file mode 100644
index 0000000000..c2f67b5818
--- /dev/null
+++ b/disk-utils/frag.8
@@ -0,0 +1,47 @@
+.\" Copyright 1992,1993,1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FRAG 8 "8 January 1994" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+frag \- simple fragmentation checker
+.SH SYNOPSIS
+.B /usr/sbin/frag
+.B "[ \-s [ \-s ]]"
+filename ...
+.SH DESCRIPTION
+.B frag
+will report the file system fragmentation on a specified
+.IR filename .
+If the
+.I filename
+is a directory,
+.B frag
+will recursively descend the directory.
+.SH OPTIONS
+.TP
+.B \-s
+Silent (may be set to 1 or 2). The first
+.B \-s
+eliminates the file by file statistics, and just prints the
+examined directories and a summary. The second
+.B \-s
+eliminates the printing of the directories, and just prints a
+summary. This option is useful when
+.B frag
+is used on a directory.
+.SH "SEE ALSO"
+.BR mkfs (8),
+.BR fsck (8),
+.BR mkefs (8),
+.BR efsck (8)
+.SH BUGS
+.B frag
+will get caught in an infinite loop in the /proc filesystem.
+.SH AUTHORS
+V1.0 by Werner Almesberger
+.br
+V1.1 by Steffen Zahn, adding directory recursion
+.br
+V1.2 by Rob Hooft, adding hole counts
+.br
+V1.3 by Steffen Zahn, ignore symlinks,
+don't cross filesys borders, get filesystem block size at runtime
diff --git a/disk-utils/frag.c b/disk-utils/frag.c
new file mode 100644
index 0000000000..0098e02f2b
--- /dev/null
+++ b/disk-utils/frag.c
@@ -0,0 +1,311 @@
+/* frag.c - simple fragmentation checker
+ V1.0 by Werner Almesberger
+ V1.1 by Steffen Zahn, adding directory recursion
+ V1.2 by Rob Hooft, adding hole counts
+ V1.3 by Steffen Zahn, email: szahn%masterix@emndev.siemens.co.at
+ 14 Nov 93
+ - ignore symlinks,
+ - don't cross filesys borders
+ - get filesystem block size at runtime
+ V1.4 by Michael Bischoff <mbi@mo.math.nat.tu-bs.de> to handle
+ indirect blocks better, but only for ext2fs
+ (applied by faith@cs.unc.edu, Sat Feb 4 22:06:27 1995)
+
+ TODO: - handle hard links
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <linux/fs.h> /* for FIBMAP */
+
+typedef struct StackElem {
+ struct StackElem *backref, *next;
+ char name[NAME_MAX];
+ char dir_seen;
+ char from_cmd_line;
+} StackElem;
+
+StackElem *top = NULL;
+
+
+void discard( void )
+{
+ StackElem *se = top;
+ if( se == NULL )
+ return ;
+ top = se->next;
+ free(se);
+}
+
+void push( StackElem * se )
+{
+ se -> next = top;
+ top = se;
+}
+
+char *p2s( StackElem *se, char *path )
+{
+ char *s;
+ if( se->backref!=NULL ) {
+ path = p2s( se->backref, path );
+ if( path[-1]!='/' )
+ *path++ = '/';
+ }
+ s = se->name;
+ while( *s )
+ *path++ = *s++;
+ return path;
+}
+
+char *path2str( StackElem *se, char *path )
+{
+ *(p2s( se, path ))=0;
+ return path;
+}
+
+void *xmalloc( size_t size )
+{
+ void *p;
+ if( (p=malloc(size))==NULL ) {
+ fprintf(stderr,"\nvirtual memory exhausted.\n");
+ exit(1);
+ }
+ return p;
+}
+
+int main(int argc,char **argv)
+{
+ int fd,last_phys_block,
+ fragments_in_file, blocks_in_file,
+ blocks,current_phys_block,
+ this_fragment, largest_fragment, i;
+ long sum_blocks=0, sum_frag_blocks=0, sum_files=0, sum_frag_files=0;
+ long num_hole=0, sum_hole=0, hole;
+ struct stat st;
+ struct statfs stfs;
+ StackElem *se, *se1;
+ char path[PATH_MAX], pathlink[PATH_MAX], *p;
+ DIR *dir;
+ struct dirent *de;
+ char silent_flag=0;
+ dev_t local_fs;
+ int block_size;
+
+ if (argc < 2)
+ {
+ fprintf(stderr,"usage: %s [-s [-s]] filename ...\n",argv[0]);
+ exit(1);
+ }
+ argc--; argv++;
+ while (argc>0)
+ {
+ p = *argv;
+ if( *p=='-' )
+ while( *++p )
+ switch( *p )
+ {
+ case 's':
+ silent_flag++; /* may be 1 or 2 */
+ break;
+ default:
+ fprintf(stderr,"\nunknown flag %c\n", *p );
+ exit(1);
+ }
+ else
+ {
+ se = xmalloc( sizeof(StackElem) );
+ se->backref=NULL; se->dir_seen=0; se->from_cmd_line=1;
+ strcpy( se->name, p );
+ push(se);
+ }
+ argc--; argv++;
+ }
+ while ( top != NULL)
+ {
+ se = top;
+ if( se->dir_seen )
+ discard();
+ else
+ {
+ path2str( se, path );
+ if( readlink( path, pathlink, sizeof(pathlink) )>=0 )
+ { /* ignore symlinks */
+ if(silent_flag<1)
+ {
+ printf("symlink %s\n", path );
+ }
+ discard();
+ }
+ else if( stat( path,&st) < 0)
+ {
+ perror( path );
+ discard();
+ }
+ else if( !se->from_cmd_line && (local_fs!=st.st_dev) )
+ { /* do not cross filesystem borders */
+ if(silent_flag<2)
+ {
+ printf("different filesystem %s\n", path );
+ }
+ discard();
+ }
+ else
+ {
+ if( se->from_cmd_line )
+ {
+ local_fs = st.st_dev;
+ if ( statfs( path, &stfs )<0 )
+ {
+ perror( path );
+ block_size = 1024;
+ }
+ else
+ block_size = stfs.f_bsize;
+ }
+ if( S_ISREG(st.st_mode)) /* regular file */
+ {
+ if ( (fd = open( path ,O_RDONLY)) < 0 )
+ {
+ perror( path );
+ discard();
+ }
+ else
+ {
+ last_phys_block = -1;
+ fragments_in_file = 0;
+ hole = 0; this_fragment=0;
+ largest_fragment=0;
+ blocks_in_file = (st.st_size+block_size-1)/block_size;
+ for (blocks = 0; blocks < blocks_in_file; blocks++)
+ {
+ current_phys_block = blocks;
+ if (ioctl(fd,FIBMAP,&current_phys_block) < 0)
+ {
+ perror(path);
+ break;
+ }
+ if (current_phys_block) { /* no hole here */
+ int indirect;
+ /* indirect is the number of indirection */
+ /* blocks which must be skipped */
+ indirect = 0;
+ /* every 256 blocks there is an indirect block,
+ the first of these is before block 12 */
+ if (blocks >= 12 && (blocks-12) % 256 == 0)
+ ++indirect;
+ /* there is a block pointing to the indirect
+ blocks every 64K blocks */
+ if (blocks >= 256+12 && (blocks-256-12) % 65536 == 0)
+ ++indirect; /* 2nd indirect block */
+ /* there is a single triple indirect block */
+ if (blocks == 65536 + 256 + 12)
+ ++indirect;
+ if (last_phys_block == current_phys_block-1-indirect)
+ this_fragment++;
+ else { /* start of first or new fragment */
+ if( largest_fragment<this_fragment )
+ largest_fragment=this_fragment;
+ this_fragment=1;
+ fragments_in_file++;
+ }
+ last_phys_block = current_phys_block;
+ }
+ else
+ {
+ hole++;
+ }
+ }
+ if( largest_fragment<this_fragment )
+ largest_fragment=this_fragment;
+ blocks_in_file-=hole;
+ /* number of allocated blocks in file */
+ if( !silent_flag )
+ {
+ if( fragments_in_file < 2
+ || blocks_in_file < 2 )
+ i = 0; /* fragmentation 0 % */
+ else
+ i = (fragments_in_file - 1) * 100 /
+ (blocks_in_file-1);
+ /* maximum fragmentation 100%
+ means every block is an fragment */
+ printf(" %3d%% %s (%d block(s), %d fragment(s), largest %d",
+ i, path, blocks_in_file,
+ fragments_in_file,largest_fragment);
+ if (hole)
+ {
+ printf(", %d hole(s))\n",hole);
+ }
+ else
+ {
+ printf(")\n");
+ }
+ }
+ sum_blocks+=blocks_in_file;
+ if (hole)
+ num_hole++;
+ sum_hole+=hole;
+ sum_files++;
+ if( fragments_in_file>1 )
+ {
+ sum_frag_blocks+=blocks_in_file-largest_fragment;
+ sum_frag_files++;
+ }
+ discard();
+ close(fd);
+ }
+ }
+ else if( S_ISDIR( st.st_mode ) ) /* push dir contents */
+ {
+ if( (dir=opendir( path ))==NULL )
+ {
+ perror(path);
+ discard();
+ }
+ else
+ {
+ if( silent_flag<2 )
+ printf("reading %s\n", path);
+ while( (de=readdir(dir))!=NULL )
+ {
+ if( (strcmp(de->d_name,".")!=0)
+ && (strcmp(de->d_name,"..")!=0) )
+ {
+ se1 = xmalloc( sizeof(StackElem) );
+ se1->backref=se; se1->dir_seen=0;
+ se1->from_cmd_line=0;
+ strcpy( se1->name, de->d_name );
+ push(se1);
+ }
+ }
+ closedir( dir );
+ se->dir_seen=1;
+ }
+ }
+ else /* if( S_ISREG(st.st_mode)) */
+ discard();
+ }
+ } /* if( se->dir_seen ) */
+ } /* while ( top != NULL) */
+ if (sum_files>1)
+ {
+ printf("\nsummary:\n");
+ printf(" %3ld%% file fragmentation (%ld of %ld files contain fragments)\n",
+ sum_files<1 ? 0L : sum_frag_files*100/sum_files,
+ sum_frag_files, sum_files);
+ printf(" %3ld%% block fragmentation (%ld of %ld blocks are in fragments)\n",
+ sum_blocks<1 ? 0L : sum_frag_blocks*100/sum_blocks,
+ sum_frag_blocks, sum_blocks);
+ if (num_hole>1)
+ printf(" %ld files contain %ld blocks in holes\n",
+ num_hole,sum_hole);
+ }
+ exit(0);
+}
diff --git a/disk-utils/fsck.minix.8 b/disk-utils/fsck.minix.8
new file mode 100644
index 0000000000..32bbe48817
--- /dev/null
+++ b/disk-utils/fsck.minix.8
@@ -0,0 +1,125 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.\" " for hilit19
+.TH FSCK 8 "10 January 1994" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+fsck.minix \- a file system consistency checker for Linux
+.SH SYNOPSIS
+.B "fsck.minix [ \-larvsmf ]"
+device
+.SH DESCRIPTION
+.B fsck.minix
+performs a consistency check for the Linux MINIX filesystem. The current
+version supports the 14 character and 30 character filename options.
+
+The program
+assumes the file system is quiescent.
+.B fsck.minix
+should not be used on a mounted device unless you can be sure nobody is
+writing to it (and remember that the kernel can write to it when it
+searches for files).
+
+The device will usually have the following form:
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+If the file system was changed (i.e., repaired), then
+.B fsck.minix
+will print "FILE SYSTEM HAS CHANGED" and will
+.BR sync (2)
+three times before exiting. Since Linux does not currently have raw
+devices, there is
+.I no
+need to reboot at this time (versus a system which
+.I does
+have raw devices).
+.SH WARNING
+.B fsck.minix
+should
+.B not
+be used on a mounted filesystem. Using
+.B fsck.minix
+on a mounted filesystem is very dangerous, due to the possibility that
+deleted files are still in use, and can seriously damage a perfectly good
+filesystem! If you absolutely have to run
+.B fsck.minix
+on a mounted filesystem (i.e., the root filesystem), make sure nothing is
+writing to the disk, and that no files are "zombies" waiting for deletion.
+.SH OPTIONS
+.TP
+.B \-l
+Lists all filenames
+.TP
+.B \-r
+Performs interactive repairs
+.TP
+.B \-a
+Performs automatic repairs (this option implies
+.BR \-r ),
+and serves to answer all of the questions asked with the default. Note
+that this can be extremely dangerous in the case of extensive file system
+damage.
+.TP
+.B \-v
+Verbose
+.TP
+.B \-s
+Outputs super-block information
+.TP
+.B \-m
+Activates MINIX-like "mode not cleared" warnings
+.TP
+.B \-f
+Force file system check even if the file system was marked as valid (this
+marking is done by the kernel when the file system is unmounted).
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR fsck.ext (8),
+.BR fsck.ext2 (8),
+.BR fsck.xiafs (8),
+.BR mkfs (8),
+.BR mkfs.minix (8),
+.BR mkfs.ext (8),
+.BR mkfs.ext2 (8),
+.BR mkfs.xiafs (8).
+.BR reboot (8)
+.SH DIAGNOSTICS
+There are numerous diagnostic messages. The ones mentioned here are the
+most commonly seen in normal usage.
+
+If the device does not exist,
+.B fsck.minix
+will print "unable to read super block". If the device exists, but is not
+a MINIX file system,
+.B fsck.minix
+will print "bad magic number in super-block".
+.SH "EXIT CODES"
+The exit code returned by
+.B fsck.minix
+is the sum of the following:
+.IP 0
+No errors
+.IP 3
+File system errors corrected, system should be rebooted if file system was
+mounted
+.IP 4
+File system errors left uncorrected
+.IP 8
+Operational error
+.IP 16
+Usage or syntax error
+.PP
+In point of fact, only 0, 3, 4, 7, 8, and 16 can ever be returned.
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
+.br
+Error code values by Rik Faith (faith@cs.unc.edu)
+.br
+Added support for file system valid flag: Dr. Wettstein
+(greg%wind.uucp@plains.nodak.edu)
diff --git a/disk-utils/fsck.minix.c b/disk-utils/fsck.minix.c
new file mode 100644
index 0000000000..209f9ce718
--- /dev/null
+++ b/disk-utils/fsck.minix.c
@@ -0,0 +1,862 @@
+/*
+ * fsck.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
+ * as per the GNU copyleft.
+ */
+
+/*
+ * 09.11.91 - made the first rudimetary functions
+ *
+ * 10.11.91 - updated, does checking, no repairs yet.
+ * Sent out to the mailing-list for testing.
+ *
+ * 14.11.91 - Testing seems to have gone well. Added some
+ * correction-code, and changed some functions.
+ *
+ * 15.11.91 - More correction code. Hopefully it notices most
+ * cases now, and tries to do something about them.
+ *
+ * 16.11.91 - More corrections (thanks to Mika Jalava). Most
+ * things seem to work now. Yeah, sure.
+ *
+ *
+ * 19.04.92 - Had to start over again from this old version, as a
+ * kernel bug ate my enhanced fsck in february.
+ *
+ * 28.02.93 - added support for different directory entry sizes..
+ *
+ * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
+ * super-block information
+ *
+ * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
+ * to that required by fsutil
+ *
+ * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
+ * Added support for file system valid flag. Also
+ * added program_version variable and output of
+ * program name and version number when program
+ * is executed.
+ *
+ * 10.12.94 - added test to prevent checking of mounted fs adapted
+ * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
+ * program. (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * I've had no time to add comments - hopefully the function names
+ * are comments enough. As with all file system checkers, this assumes
+ * the file system is quiescent - don't use it on a mounted device
+ * unless you can be sure nobody is writing to it (and remember that the
+ * kernel can write to it when it searches for files).
+ *
+ * Usuage: fsck [-larvsm] device
+ * -l for a listing of all the filenames
+ * -a for automatic repairs (not implemented)
+ * -r for repairs (interactive) (not implemented)
+ * -v for verbose (tells how many files)
+ * -s for super-block info
+ * -m for minix-like "mode not cleared" warnings
+ * -f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <mntent.h>
+#include <sys/stat.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+#ifndef __GNUC__
+#error "needs gcc for the bitop-__asm__'s"
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define ROOT_INO 1
+
+#define UPPER(size,n) ((size+((n)-1))/(n))
+#define INODE_SIZE (sizeof(struct minix_inode))
+#define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK)
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+
+#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
+
+static char * program_name = "fsck.minix";
+static char * program_version = "1.0 - 12/30/93";
+static char * device_name = NULL;
+static int IN;
+static int repair=0, automatic=0, verbose=0, list=0, show=0, warn_mode=0,
+ force=0;
+static int directory=0, regular=0, blockdev=0, chardev=0, links=0,
+ symlinks=0, total=0;
+
+static int changed = 0; /* flags if the filesystem has been changed */
+static int errors_uncorrected = 0; /* flag if some error was not corrected */
+static int dirsize = 16;
+static int namelen = 14;
+
+/* File-name data */
+#define MAX_DEPTH 50
+static int name_depth = 0;
+static char name_list[MAX_DEPTH][NAME_MAX+1];
+
+static char * inode_buffer = NULL;
+#define Inode (((struct minix_inode *) inode_buffer)-1)
+static char super_block_buffer[BLOCK_SIZE];
+#define Super (*(struct minix_super_block *)super_block_buffer)
+#define INODES ((unsigned long)Super.s_ninodes)
+#define ZONES ((unsigned long)Super.s_nzones)
+#define IMAPS ((unsigned long)Super.s_imap_blocks)
+#define ZMAPS ((unsigned long)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
+#define ZONESIZE ((unsigned long)Super.s_log_zone_size)
+#define MAXSIZE ((unsigned long)Super.s_max_size)
+#define MAGIC (Super.s_magic)
+#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
+
+static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS];
+static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS];
+
+static unsigned char * inode_count = NULL;
+static unsigned char * zone_count = NULL;
+
+void recursive_check(unsigned int ino);
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op "l %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+#define inode_in_use(x) (bit(inode_map,(x)))
+#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
+
+#define mark_inode(x) (setbit(inode_map,(x)),changed=1)
+#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
+
+#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1)
+#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1)
+
+/*
+ * Volatile to let gcc know that this doesn't return. When trying
+ * to compile this under minix, volatile gives a warning, as
+ * exit() isn't defined as volatile under minix.
+ */
+volatile void fatal_error(const char * fmt_string, int status)
+{
+ fprintf(stderr,fmt_string,program_name,device_name);
+ exit(status);
+}
+
+#define usage() fatal_error("Usage: %s [-larvsmf] /dev/name\n",16)
+#define die(str) fatal_error("%s: " str "\n",8)
+
+/*
+ * This simply goes through the file-name data and prints out the
+ * current file.
+ */
+void print_current_name(void)
+{
+ int i=0;
+
+ while (i<name_depth)
+ printf("/%.*s",namelen,name_list[i++]);
+}
+
+int ask(const char * string,int def)
+{
+ int c;
+
+ if (!repair) {
+ printf("\n");
+ errors_uncorrected = 1;
+ return 0;
+ }
+ if (automatic) {
+ printf("\n");
+ if (!def)
+ errors_uncorrected = 1;
+ return def;
+ }
+ printf(def?"%s (y/n)? ":"%s (n/y)? ",string);
+ for (;;) {
+ fflush(stdout);
+ if ((c=getchar())==EOF) {
+ if (!def)
+ errors_uncorrected = 1;
+ return def;
+ }
+ c=toupper(c);
+ if (c == 'Y') {
+ def = 1;
+ break;
+ } else if (c == 'N') {
+ def = 0;
+ break;
+ } else if (c == ' ' || c == '\n')
+ break;
+ }
+ if (def)
+ printf("y\n");
+ else {
+ printf("n\n");
+ errors_uncorrected = 1;
+ }
+ return def;
+}
+
+/*
+ * Make certain that we aren't checking a filesystem that is on a
+ * mounted partition. Code adapted from e2fsck, Copyright (C) 1993,
+ * 1994 Theodore Ts'o. Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+ FILE * f;
+ struct mntent * mnt;
+ int cont;
+ int fd;
+
+ if ((f = setmntent (MOUNTED, "r")) == NULL)
+ return;
+ while ((mnt = getmntent (f)) != NULL)
+ if (strcmp (device_name, mnt->mnt_fsname) == 0)
+ break;
+ endmntent (f);
+ if (!mnt)
+ return;
+
+ /*
+ * If the root is mounted read-only, then /etc/mtab is
+ * probably not correct; so we won't issue a warning based on
+ * it.
+ */
+ fd = open(MOUNTED, O_RDWR);
+ if (fd < 0 && errno == EROFS)
+ return;
+ else
+ close(fd);
+
+ printf ("%s is mounted. ", device_name);
+ if (isatty(0) && isatty(1))
+ cont = ask("Do you really want to continue", 0);
+ else
+ cont = 0;
+ if (!cont) {
+ printf ("check aborted.\n");
+ exit (0);
+ }
+ return;
+}
+
+/*
+ * check_zone_nr checks to see that *nr is a valid zone nr. If it
+ * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
+ * if an error was corrected, and returns the zone (0 for no zone
+ * or a bad zone-number).
+ */
+int check_zone_nr(unsigned short * nr, int * corrected)
+{
+ if (!*nr)
+ return 0;
+ if (*nr < FIRSTZONE)
+ printf("Zone nr < FIRSTZONE in file `");
+ else if (*nr >= ZONES)
+ printf("Zone nr >= ZONES in file `");
+ else
+ return *nr;
+ print_current_name();
+ printf("'.");
+ if (ask("Remove block",1)) {
+ *nr = 0;
+ *corrected = 1;
+ }
+ return 0;
+}
+
+/*
+ * read-block reads block nr into the buffer at addr.
+ */
+void read_block(unsigned int nr, char * addr)
+{
+ if (!nr) {
+ memset(addr,0,BLOCK_SIZE);
+ return;
+ }
+ if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET)) {
+ printf("Read error: unable to seek to block in file '");
+ print_current_name();
+ printf("'\n");
+ memset(addr,0,BLOCK_SIZE);
+ errors_uncorrected = 1;
+ } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
+ printf("Read error: bad block in file '");
+ print_current_name();
+ printf("'\n");
+ memset(addr,0,BLOCK_SIZE);
+ errors_uncorrected = 1;
+ }
+}
+
+/*
+ * write_block writes block nr to disk.
+ */
+void write_block(unsigned int nr, char * addr)
+{
+ if (!nr)
+ return;
+ if (nr < FIRSTZONE || nr >= ZONES) {
+ printf("Internal error: trying to write bad block\n"
+ "Write request ignored\n");
+ errors_uncorrected = 1;
+ return;
+ }
+ if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET))
+ die("seek failed in write_block");
+ if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
+ printf("Write error: bad block in file '");
+ print_current_name();
+ printf("'\n");
+ errors_uncorrected = 1;
+ }
+}
+
+/*
+ * map-block calculates the absolute block nr of a block in a file.
+ * It sets 'changed' if the inode has needed changing, and re-writes
+ * any indirect blocks with errors.
+ */
+int map_block(struct minix_inode * inode, unsigned int blknr)
+{
+ unsigned short ind[BLOCK_SIZE>>1];
+ unsigned short dind[BLOCK_SIZE>>1];
+ int blk_chg, block, result;
+
+ if (blknr<7)
+ return check_zone_nr(inode->i_zone + blknr, &changed);
+ blknr -= 7;
+ if (blknr<512) {
+ block = check_zone_nr(inode->i_zone + 7, &changed);
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr(blknr + ind, &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+ }
+ blknr -= 512;
+ block = check_zone_nr(inode->i_zone + 8, &changed);
+ read_block(block, (char *) dind);
+ blk_chg = 0;
+ result = check_zone_nr(dind + (blknr/512), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) dind);
+ block = result;
+ read_block(block, (char *) ind);
+ blk_chg = 0;
+ result = check_zone_nr(ind + (blknr%512), &blk_chg);
+ if (blk_chg)
+ write_block(block, (char *) ind);
+ return result;
+}
+
+void write_super_block(void)
+{
+ /*
+ * Set the state of the filesystem based on whether or not there
+ * are uncorrected errors. The filesystem valid flag is
+ * unconditionally set if we get this far.
+ */
+ Super.s_state |= MINIX_VALID_FS;
+ if ( errors_uncorrected )
+ Super.s_state |= MINIX_ERROR_FS;
+ else
+ Super.s_state &= ~MINIX_ERROR_FS;
+
+ if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+ die("seek failed in write_super_block");
+ if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE))
+ die("unable to write super-block");
+
+ return;
+}
+
+void write_tables(void)
+{
+ write_super_block();
+
+ if (IMAPS*BLOCK_SIZE != write(IN,inode_map,IMAPS*BLOCK_SIZE))
+ die("Unable to write inode map");
+ if (ZMAPS*BLOCK_SIZE != write(IN,zone_map,ZMAPS*BLOCK_SIZE))
+ die("Unable to write zone map");
+ if (INODE_BUFFER_SIZE != write(IN,inode_buffer,INODE_BUFFER_SIZE))
+ die("Unable to write inodes");
+}
+
+void read_tables(void)
+{
+ memset(inode_map,0,sizeof(inode_map));
+ memset(zone_map,0,sizeof(zone_map));
+ if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+ die("seek failed");
+ if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE))
+ die("unable to read super block");
+ if (MAGIC == MINIX_SUPER_MAGIC) {
+ namelen = 14;
+ dirsize = 16;
+ } else if (MAGIC == MINIX_SUPER_MAGIC2) {
+ namelen = 30;
+ dirsize = 32;
+ } else
+ die("bad magic number in super-block");
+ if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
+ die("Only 1k blocks/zones supported");
+ if (!IMAPS || IMAPS > MINIX_I_MAP_SLOTS)
+ die("bad s_imap_blocks field in super-block");
+ if (!ZMAPS || ZMAPS > MINIX_Z_MAP_SLOTS)
+ die("bad s_zmap_blocks field in super-block");
+ inode_buffer = malloc(INODE_BUFFER_SIZE);
+ if (!inode_buffer)
+ die("Unable to allocate buffer for inodes");
+ inode_count = malloc(INODES);
+ if (!inode_count)
+ die("Unable to allocate buffer for inode count");
+ zone_count = malloc(ZONES);
+ if (!zone_count)
+ die("Unable to allocate buffer for zone count");
+ if (IMAPS*BLOCK_SIZE != read(IN,inode_map,IMAPS*BLOCK_SIZE))
+ die("Unable to read inode map");
+ if (ZMAPS*BLOCK_SIZE != read(IN,zone_map,ZMAPS*BLOCK_SIZE))
+ die("Unable to read zone map");
+ if (INODE_BUFFER_SIZE != read(IN,inode_buffer,INODE_BUFFER_SIZE))
+ die("Unable to read inodes");
+ if (NORM_FIRSTZONE != FIRSTZONE) {
+ printf("Warning: Firstzone != Norm_firstzone\n");
+ errors_uncorrected = 1;
+ }
+ if (show) {
+ printf("%d inodes\n",INODES);
+ printf("%d blocks\n",ZONES);
+ printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE);
+ printf("Zonesize=%d\n",BLOCK_SIZE<<ZONESIZE);
+ printf("Maxsize=%d\n",MAXSIZE);
+ printf("Filesystem state=%d\n", Super.s_state);
+ printf("namelen=%d\n\n",namelen);
+ }
+}
+
+struct minix_inode * get_inode(unsigned int nr)
+{
+ struct minix_inode * inode;
+
+ if (!nr || nr >= INODES)
+ return NULL;
+ total++;
+ inode = Inode + nr;
+ if (!inode_count[nr]) {
+ if (!inode_in_use(nr)) {
+ printf("Inode %d marked not used, but used for file '",
+ nr);
+ print_current_name();
+ printf("'\n");
+ if (repair)
+ if (ask("Mark in use",1))
+ mark_inode(nr);
+ else
+ errors_uncorrected = 1;
+ }
+ if (S_ISDIR(inode->i_mode))
+ directory++;
+ else if (S_ISREG(inode->i_mode))
+ regular++;
+ else if (S_ISCHR(inode->i_mode))
+ chardev++;
+ else if (S_ISBLK(inode->i_mode))
+ blockdev++;
+ else if (S_ISLNK(inode->i_mode))
+ symlinks++;
+ else if (S_ISSOCK(inode->i_mode))
+ ;
+ else if (S_ISFIFO(inode->i_mode))
+ ;
+ else {
+ print_current_name();
+ printf(" has mode %05o\n",inode->i_mode);
+ }
+
+ } else
+ links++;
+ if (!++inode_count[nr]) {
+ printf("Warning: inode count too big.\n");
+ inode_count[nr]--;
+ errors_uncorrected = 1;
+ }
+ return inode;
+}
+
+void check_root(void)
+{
+ struct minix_inode * inode = Inode + ROOT_INO;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ die("root inode isn't a directory");
+}
+
+static int add_zone(unsigned short * znr, int * corrected)
+{
+ int result;
+ int block;
+
+ result = 0;
+ block = check_zone_nr(znr, corrected);
+ if (!block)
+ return 0;
+ if (zone_count[block]) {
+ printf("Block has been used before. Now in file `");
+ print_current_name();
+ printf("'.");
+ if (ask("Clear",1)) {
+ *znr = 0;
+ block = 0;
+ *corrected = 1;
+ }
+ }
+ if (!block)
+ return 0;
+ if (!zone_in_use(block)) {
+ printf("Block %d in file `",block);
+ print_current_name();
+ printf("' is marked not in use.");
+ if (ask("Correct",1))
+ mark_zone(block);
+ }
+ if (!++zone_count[block])
+ zone_count[block]--;
+ return block;
+}
+
+static void add_zone_ind(unsigned short * znr, int * corrected)
+{
+ static char blk[BLOCK_SIZE];
+ int i, chg_blk=0;
+ int block;
+
+ block = add_zone(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
+ add_zone(i + (unsigned short *) blk, &chg_blk);
+ if (chg_blk)
+ write_block(block, blk);
+}
+
+static void add_zone_dind(unsigned short * znr, int * corrected)
+{
+ static char blk[BLOCK_SIZE];
+ int i, blk_chg=0;
+ int block;
+
+ block = add_zone(znr, corrected);
+ if (!block)
+ return;
+ read_block(block, blk);
+ for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
+ add_zone_ind(i + (unsigned short *) blk, &blk_chg);
+ if (blk_chg)
+ write_block(block, blk);
+}
+
+void check_zones(unsigned int i)
+{
+ struct minix_inode * inode;
+
+ if (!i || i >= INODES)
+ return;
+ if (inode_count[i] > 1) /* have we counted this file already? */
+ return;
+ inode = Inode + i;
+ if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
+ !S_ISLNK(inode->i_mode))
+ return;
+ for (i=0 ; i<7 ; i++)
+ add_zone(i + inode->i_zone, &changed);
+ add_zone_ind(7 + inode->i_zone, &changed);
+ add_zone_dind(8 + inode->i_zone, &changed);
+}
+
+void check_file(struct minix_inode * dir, unsigned int offset)
+{
+ static char blk[BLOCK_SIZE];
+ struct minix_inode * inode;
+ int ino;
+ char * name;
+ int block;
+
+ block = map_block(dir,offset/BLOCK_SIZE);
+ read_block(block, blk);
+ name = blk + (offset % BLOCK_SIZE) + 2;
+ ino = * (unsigned short *) (name-2);
+ if (ino >= INODES) {
+ print_current_name();
+ printf(" contains a bad inode number for file '");
+ printf("%.*s'.",namelen,name);
+ if (ask(" Remove",1)) {
+ *(unsigned short *)(name-2) = 0;
+ write_block(block, blk);
+ }
+ ino = 0;
+ }
+ inode = get_inode(ino);
+ if (!offset)
+ if (!inode || strcmp(".",name)) {
+ print_current_name();
+ printf(": bad directory: '.' isn't first\n");
+ errors_uncorrected = 1;
+ } else return;
+ if (offset == dirsize)
+ if (!inode || strcmp("..",name)) {
+ print_current_name();
+ printf(": bad directory: '..' isn't second\n");
+ errors_uncorrected = 1;
+ } else return;
+ if (!inode)
+ return;
+ if (name_depth < MAX_DEPTH)
+ strncpy(name_list[name_depth],name,namelen);
+ name_depth++;
+ if (list) {
+ if (verbose)
+ printf("%6d %07o %3d ",ino,inode->i_mode,inode->i_nlinks);
+ print_current_name();
+ if (S_ISDIR(inode->i_mode))
+ printf(":\n");
+ else
+ printf("\n");
+ }
+ check_zones(ino);
+ if (inode && S_ISDIR(inode->i_mode))
+ recursive_check(ino);
+ name_depth--;
+ return;
+}
+
+void recursive_check(unsigned int ino)
+{
+ struct minix_inode * dir;
+ unsigned int offset;
+
+ dir = Inode + ino;
+ if (!S_ISDIR(dir->i_mode))
+ die("internal error");
+ if (dir->i_size < 32) {
+ print_current_name();
+ printf(": bad directory: size<32");
+ errors_uncorrected = 1;
+ }
+ for (offset = 0 ; offset < dir->i_size ; offset += dirsize)
+ check_file(dir,offset);
+}
+
+int bad_zone(int i)
+{
+ char buffer[1024];
+
+ if (BLOCK_SIZE*i != lseek(IN, BLOCK_SIZE*i, SEEK_SET))
+ die("seek failed in bad_zone");
+ return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
+}
+
+void check_counts(void)
+{
+ int i;
+
+ for (i=1 ; i < INODES ; i++) {
+ if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
+ printf("Inode %d mode not cleared.",i);
+ if (ask("Clear",1)) {
+ Inode[i].i_mode = 0;
+ changed = 1;
+ }
+ }
+ if (!inode_count[i]) {
+ if (!inode_in_use(i))
+ continue;
+ printf("Inode %d not used, marked used in the bitmap.",i);
+ if (ask("Clear",1))
+ unmark_inode(i);
+ continue;
+ }
+ if (!inode_in_use(i)) {
+ printf("Inode %d used, marked unused in the bitmap.",
+ i);
+ if (ask("Set",1))
+ mark_inode(i);
+ }
+ if (Inode[i].i_nlinks != inode_count[i]) {
+ printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
+ i,Inode[i].i_mode,Inode[i].i_nlinks,inode_count[i]);
+ if (ask("Set i_nlinks to count",1)) {
+ Inode[i].i_nlinks=inode_count[i];
+ changed=1;
+ }
+ }
+ }
+ for (i=FIRSTZONE ; i < ZONES ; i++) {
+ if (zone_in_use(i) == zone_count[i])
+ continue;
+ if (!zone_count[i]) {
+ if (bad_zone(i))
+ continue;
+ printf("Zone %d: marked in use, no file uses it.",i);
+ if (ask("Unmark",1))
+ unmark_zone(i);
+ continue;
+ }
+ printf("Zone %d: %sin use, counted=%d\n",
+ i,zone_in_use(i)?"":"not ",zone_count[i]);
+ }
+}
+
+void check(void)
+{
+ memset(inode_count,0,INODES*sizeof(*inode_count));
+ memset(zone_count,0,ZONES*sizeof(*zone_count));
+ check_zones(ROOT_INO);
+ recursive_check(ROOT_INO);
+ check_counts();
+}
+
+int main(int argc, char ** argv)
+{
+ struct termios termios,tmp;
+ int count;
+ int retcode = 0;
+
+ if (argc && *argv)
+ program_name = *argv;
+ if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
+ die("bad inode size");
+ while (argc-- > 1) {
+ argv++;
+ if (argv[0][0] != '-')
+ if (device_name)
+ usage();
+ else
+ device_name = argv[0];
+ else while (*++argv[0])
+ switch (argv[0][0]) {
+ case 'l': list=1; break;
+ case 'a': automatic=1; repair=1; break;
+ case 'r': automatic=0; repair=1; break;
+ case 'v': verbose=1; break;
+ case 's': show=1; break;
+ case 'm': warn_mode=1; break;
+ case 'f': force=1; break;
+ default: usage();
+ }
+ }
+ if (!device_name)
+ usage();
+ check_mount(); /* trying to check a mounted filesystem? */
+ if (repair && !automatic) {
+ if (!isatty(0) || !isatty(1))
+ die("need terminal for interactive repairs");
+ tcgetattr(0,&termios);
+ tmp = termios;
+ tmp.c_lflag &= ~(ICANON|ECHO);
+ tcsetattr(0,TCSANOW,&tmp);
+ }
+ IN = open(device_name,repair?O_RDWR:O_RDONLY);
+ if (IN < 0)
+ die("unable to open '%s'");
+ for (count=0 ; count<3 ; count++)
+ sync();
+ read_tables();
+
+ /*
+ * Determine whether or not we should continue with the checking.
+ * This is based on the status of the filesystem valid and error
+ * flags and whether or not the -f switch was specified on the
+ * command line.
+ */
+ printf("%s, %s\n", program_name, program_version);
+ if ( !(Super.s_state & MINIX_ERROR_FS) &&
+ (Super.s_state & MINIX_VALID_FS) &&
+ !force ) {
+ if (repair)
+ printf("%s is clean, no check.\n", device_name);
+ if (repair && !automatic)
+ tcsetattr(0,TCSANOW,&termios);
+ return retcode;
+ }
+ else if (force)
+ printf("Forcing filesystem check on %s.\n", device_name);
+ else if (repair)
+ printf("Filesystem on %s is dirty, needs checking.\n",\
+ device_name);
+
+ check_root();
+ check();
+ if (verbose) {
+ int i, free;
+
+ for (i=1,free=0 ; i < INODES ; i++)
+ if (!inode_in_use(i))
+ free++;
+ printf("\n%6d inodes used (%d%%)\n",(INODES-free-1),
+ 100*(INODES-free-1)/(INODES-1));
+ for (i=FIRSTZONE,free=0 ; i < ZONES ; i++)
+ if (!zone_in_use(i))
+ free++;
+ printf("%6d zones used (%d%%)\n",(ZONES-free),
+ 100*(ZONES-free)/ZONES);
+ printf("\n%6d regular files\n"
+ "%6d directories\n"
+ "%6d character device files\n"
+ "%6d block device files\n"
+ "%6d links\n"
+ "%6d symbolic links\n"
+ "------\n"
+ "%6d files\n",
+ regular,directory,chardev,blockdev,
+ links-2*directory+1,symlinks,total-2*directory+1);
+ }
+ if (changed) {
+ write_tables();
+ printf( "----------------------------\n"
+ "FILE SYSTEM HAS BEEN CHANGED\n"
+ "----------------------------\n");
+ for (count=0 ; count<3 ; count++)
+ sync();
+ }
+ else if ( repair )
+ write_super_block();
+
+ if (repair && !automatic)
+ tcsetattr(0,TCSANOW,&termios);
+
+ if (changed)
+ retcode += 3;
+ if (errors_uncorrected)
+ retcode += 4;
+ return retcode;
+}
diff --git a/disk-utils/llseek.c b/disk-utils/llseek.c
new file mode 100644
index 0000000000..66166d3a89
--- /dev/null
+++ b/disk-utils/llseek.c
@@ -0,0 +1,84 @@
+/*
+ * 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>
+#include <linux/unistd.h>
+#if 0
+#include "et/com_err.h"
+#include "ext2fs/io.h"
+#endif
+
+#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);
+
+#ifdef __linux__
+
+#ifndef __NR__llseek
+#define __NR__llseek 140
+#endif
+
+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)
+
+ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset,
+ unsigned int origin)
+{
+ unsigned long offset_high;
+ unsigned long offset_low;
+ ext2_loff_t result;
+ int retval;
+ static int do_compat = 0;
+
+ if (do_compat)
+ return lseek (fd, (off_t) offset, origin);
+
+ offset_high = ((unsigned long long) offset) >> 32;
+ offset_low = ((unsigned long long) offset) & 0xffffffff;
+ retval = _llseek (fd, offset_high, offset_low, &result, origin);
+ if (retval == -1 && errno == ENOSYS) {
+ /*
+ * Just in case this code runs on top of an old kernel
+ * which does not support the llseek system call
+ */
+ do_compat++;
+ return lseek (fd, (off_t) offset, origin);
+ }
+ if (retval == -1)
+ result = -1;
+ return result;
+}
+
+#else
+
+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
+
+
diff --git a/disk-utils/mkfs.8 b/disk-utils/mkfs.8
new file mode 100644
index 0000000000..48b223425d
--- /dev/null
+++ b/disk-utils/mkfs.8
@@ -0,0 +1,133 @@
+.\" -*- nroff -*-
+.TH FSCK 8 "Jul 1993" "Version 1.8"
+.SH NAME
+fsck \- check and repair a Linux file system
+.SH SYNOPSIS
+.B fsck
+[
+.B \-A
+]
+[
+.B \-V
+]
+[
+.B \-t
+.I fstype
+]
+[
+.B fs-options
+]
+.I filesys
+.SH DESCRIPTION
+.B fsck
+is used to check and optionally repair a Linux file system.
+.I filesys
+is either the device name (e.g. /dev/hda1, /dev/sdb2) or
+the mount point (e.g. /, /usr, /home) for the file system.
+.PP
+The exit code returned by
+.B fsck
+is the sum of the following conditions:
+.br
+\ 0\ \-\ No errors
+.br
+\ 1\ \-\ File system errors corrected
+.br
+\ 2\ \-\ File system errors corrected, system should
+.br
+\ \ \ \ be rebooted if file system was mounted
+.br
+\ 4\ \-\ File system errors left uncorrected
+.br
+\ 8\ \-\ Operational error
+.br
+\ 16\ \-\ Usage or syntax error
+.br
+\ 128\ \-\ Shared library error
+.br
+The exit code returned when all file systems are checked using the
+.B -A
+option is the bit-wise OR of the exit codes for each
+file system that is checked.
+.PP
+In actuality,
+.B fsck
+is simply a front-end for the various file system checkers
+(\fBfsck\fR.\fIfstype\fR)
+available under Linux.
+The file system-specific checker is searched for in /etc/fs first,
+then in /etc and finally in the directories listed in the PATH
+environment variable.
+Please see the file system-specific checker manual pages for
+further details.
+.SH OPTIONS
+.TP
+.B -A
+Walk through the
+.I /etc/fstab
+file and try to check all file systems in one run. This option is
+typically used from the
+.I /etc/rc
+system initalization file, instead of multiple commands for checking
+a single file system. Note, that with this option, you cannot give
+the
+.I filesys
+argument as well.
+.TP
+.B -V
+Produce verbose output, including all file system-specific commands
+that are executed.
+Specifying this option more than once inhibits execution of any
+file system-specific commands.
+This is really only useful for testing.
+.TP
+.BI -t \ fstype
+Specifies the type of file system to be checked.
+If not specified, the type is deduced by searching for
+.I filesys
+in
+.I /etc/fstab
+and using the corresponding entry.
+If the type can not be deduced, the default file system type
+(currently minix) is used.
+.TP
+.B fs-options
+File system-specific options to be passed to the real file
+system checker.
+Although not guaranteed, the following options are supported
+by most file system checkers.
+.TP
+.I -a
+Automatically repair the file system without any questions (use
+this option with caution).
+.TP
+.I -l
+List all the file names in the file system.
+.TP
+.I -r
+Interactively repair the file system (ask for confirmations).
+.TP
+.I -s
+List the super block before checking the file system.
+.TP
+.I -v
+Produce verbose output.
+.SH BUGS
+All generic options must precede and not be combined with
+file system-specific options.
+Some file system-specific programs do not support the
+.I -v
+(verbose) option, nor return meaningful exit codes.
+.SH AUTHORS
+David Engel (david@ods.com)
+.br
+Fred N. van Kempen (waltje@uwalt.nl.mugnet.org)
+.br
+The manual page was shamelessly adapted from Remy Card's version
+for the ext2 file system.
+.SH SEE ALSO
+.BR mkfs (8),
+.BR fsck.minix (8),
+.BR fsck.ext (8),
+.BR fsck.ext2 (8),
+.BR fsck.xiafs (8).
diff --git a/disk-utils/mkfs.c b/disk-utils/mkfs.c
new file mode 100644
index 0000000000..018a538491
--- /dev/null
+++ b/disk-utils/mkfs.c
@@ -0,0 +1,297 @@
+/*
+ * fs-util A simple generic frontend for the for the fsck and mkfs
+ * programs under Linux. See the manual pages for details.
+ *
+ * Usage: fsck [-AV] [-t fstype] [fs-options] device
+ * mkfs [-V] [-t fstype] [fs-options] device< [size]
+ *
+ * Authors: David Engel, <david@ods.com>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ */
+
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mntent.h>
+#include <unistd.h>
+#include <getopt.h>
+
+
+#ifndef DEFAULT_FSTYPE
+# define DEFAULT_FSTYPE "minix"
+#endif
+
+#define _PATH_PROG "%s.%s"
+#define _PROG_FSCK "fsck"
+
+#define EXIT_OK 0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT 2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR 8
+#define EXIT_USAGE 16
+#define EXIT_LIBRARY 128
+
+static char *Version = "1.8";
+static char *ignored_types[] = {
+ "ignore",
+ "iso9660",
+ "msdos",
+ "nfs",
+ "proc",
+ "sw",
+ "swap",
+ NULL
+};
+
+
+/* Execute a program. */
+int do_exec(char *prog, char **argv, int verbose)
+{
+ char *args[33];
+ register int i;
+ int pid, status;
+
+ /* Build the vector. */
+ i = 0;
+ args[i++] = prog;
+ while(*argv != NULL && i < 32)
+ args[i++] = *argv++;
+ args[i] = NULL;
+
+ if (verbose) {
+ i = 0;
+ while(args[i] != NULL) {
+ printf("%s ", args[i]);
+ i++;
+ }
+ printf("\n");
+ if (verbose > 1)
+ return EXIT_OK;
+ }
+
+ /* Fork and execute the correct program. */
+ if ((pid = fork()) < 0) {
+ perror("fork");
+ status = EXIT_ERROR;
+ } else if (pid == 0) {
+ (void) execvp(args[0], args);
+ perror(args[0]);
+ exit(EXIT_ERROR);
+ } else {
+ while(wait(&status) != pid)
+ ;
+ status = WEXITSTATUS(status);
+ }
+
+ return status;
+}
+
+
+/* Check if we have to ignore a file system type. */
+int ignore(char *type, char *opts)
+{
+ char *cp;
+ char **ip;
+
+ ip = ignored_types;
+ while (*ip != NULL) {
+ if (!strcmp(type, *ip))
+ return 1;
+ ip++;
+ }
+
+ for (cp = strtok(opts, ","); cp != NULL; cp = strtok(NULL, ",")) {
+ if (!strcmp(cp, "noauto"))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Check all file systems, using the /etc/fstab table. */
+int check_all(int verbose, char **argv)
+{
+ char path[PATH_MAX];
+ char *args[33];
+ FILE *mntfile;
+ struct mntent *mp;
+ register int i;
+ int status = EXIT_OK;
+
+ if (verbose)
+ printf("Checking all file systems.\n");
+
+ /* Create an array of arguments. */
+ i = 0;
+ while (*argv != NULL && i < 32)
+ args[i++] = *argv++;
+ args[i] = NULL;
+ args[i + 1] = NULL;
+
+ /* Open the mount table. */
+ if ((mntfile = setmntent(MNTTAB, "r")) == NULL) {
+ perror(MNTTAB);
+ exit(EXIT_ERROR);
+ }
+
+ /* Walk through the /etc/fstab file. */
+ while ((mp = getmntent(mntfile)) != NULL) {
+ if (verbose)
+ printf("%-7s %-15s %-15s ", mp->mnt_type,
+ mp->mnt_fsname, mp->mnt_dir);
+ if (ignore(mp->mnt_type, mp->mnt_opts)) {
+ if (verbose)
+ printf("(ignored)\n");
+ continue;
+ }
+
+ /* Build program name. */
+ sprintf(path, _PATH_PROG, _PROG_FSCK, mp->mnt_type);
+ args[i] = mp->mnt_fsname;
+ status |= do_exec(path, args, verbose);
+ }
+
+ (void) endmntent(mntfile);
+
+ return status;
+}
+
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+struct mntent *lookup(char *filesys)
+{
+ FILE *mntfile;
+ struct mntent *mp;
+
+ /* No filesys name given. */
+ if (filesys == NULL)
+ return NULL;
+
+ /* Open the mount table. */
+ if ((mntfile = setmntent(MNTTAB, "r")) == NULL) {
+ perror(MNTTAB);
+ exit(EXIT_ERROR);
+ }
+
+ while ((mp = getmntent(mntfile)) != NULL) {
+ if (!strcmp(filesys, mp->mnt_fsname) ||
+ !strcmp(filesys, mp->mnt_dir))
+ break;
+ }
+
+ (void) endmntent(mntfile);
+
+ return mp;
+}
+
+
+void usage(int fsck, char *prog)
+{
+ if (fsck) {
+ fprintf(stderr, "Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n");
+ } else {
+ fprintf(stderr, "Usage: mkfs [-V] [-t fstype] [fs-options] filesys [size]\n");
+ }
+
+ exit(EXIT_USAGE);
+}
+
+
+void main(int argc, char *argv[])
+{
+ char path[PATH_MAX];
+ char *oldpath, newpath[PATH_MAX];
+ register char *sp;
+ struct mntent *fsent;
+ char *fstype = NULL;
+ int verbose = 0;
+ int doall = 0;
+ int i, fsck, more;
+
+ /* Must be 1 for "fsck" and 0 for "mkfs". */
+ if ((sp = strrchr(argv[0], '/')) != NULL)
+ sp++;
+ else
+ sp = argv[0];
+ if (!strcmp(sp, _PROG_FSCK))
+ fsck = 1;
+ else
+ fsck = 0;
+
+ /* Check commandline options. */
+ opterr = 0;
+ more = 0;
+ while ((more == 0) && ((i = getopt(argc, argv, "AVt:")) != EOF))
+ switch(i) {
+ case 'A':
+ doall++;
+ break;
+ case 'V':
+ verbose++;
+ break;
+ case 't':
+ if (optarg == NULL)
+ usage(fsck, sp);
+ fstype = optarg;
+ break;
+ default:
+ more = 1;
+ break; /* start of specific arguments */
+ }
+
+ /* Did we get any specific arguments? */
+ if (more)
+ optind--;
+
+ /* Print our version number if requested. */
+ if (verbose)
+ printf("%s (fsutil) version %s (%s)\n", argv[0],
+ Version, __DATE__);
+
+ /* Update our PATH to include /etc/fs and /etc. */
+ strcpy(newpath, "PATH=/etc/fs:/etc:");
+ if ((oldpath = getenv("PATH")) != NULL)
+ strcat(newpath, oldpath);
+ putenv(newpath);
+
+ /* If -A was specified ("check all"), double-check. */
+ if (doall) {
+ if (!fsck || (fstype != NULL))
+ usage(fsck, sp);
+ exit(check_all(verbose, &argv[optind]));
+ } else {
+ /* If -t wasn't specified, we must deduce fstype. */
+ if (fstype == NULL) {
+ /* make sure that "filesys" was specified */
+ if (optind >= argc)
+ usage(fsck, sp);
+ /* then try looking for it in /etc/fstab */
+ if ((fsent = lookup(argv[argc - 1])) != NULL) {
+ argv[argc - 1] = fsent->mnt_fsname;
+ fstype = fsent->mnt_type;
+ } else {
+ if (!fsck && optind < argc-1) {
+ if ((fsent = lookup(argv[argc - 2])) != NULL) {
+ argv[argc - 2] = fsent->mnt_fsname;
+ fstype = fsent->mnt_type;
+ }
+ }
+ }
+ /* if we still don't know, use the default */
+ if (fstype == NULL) fstype = DEFAULT_FSTYPE;
+ }
+
+ /* Build program name. */
+ sprintf(path, _PATH_PROG, sp, fstype);
+ exit(do_exec(path, &argv[optind], verbose));
+ }
+ /*NOTREACHED*/
+}
diff --git a/disk-utils/mkfs.minix.8 b/disk-utils/mkfs.minix.8
new file mode 100644
index 0000000000..cbfb1cf4ca
--- /dev/null
+++ b/disk-utils/mkfs.minix.8
@@ -0,0 +1,88 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.\" " for emacs hilit19 mode
+.TH MKFS 8 "10 January 1994" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+mkfs \- make a Linux MINIX filesystem
+.SH SYNOPSIS
+.BR "mkfs [ \-c ] [ \-n"
+namelength
+.B ] [ \-i
+inodecount
+.B ]
+device size-in-blocks
+.br
+.B "mkfs [ \-l"
+filename
+.B ]
+device size-in-blocks
+.SH DESCRIPTION
+.B mkfs
+creates a Linux MINIX file-system on a device (usually a disk partition).
+
+The
+.I device
+is usually of the following form:
+
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+The
+.I size-in-blocks
+parameter is the desired size of the file system, in blocks. This
+information can be determined from the
+.BR fdisk (8)
+program. Only block counts strictly greater than 10 and strictly less than
+65536 are allowed.
+.SH OPTIONS
+.TP
+.B \-c
+Check the device for bad blocks before creating the file system. If any
+are found, the count is printed.
+.TP
+.BI \-n " namelength"
+Specify the maximum length of filenames. No space is allowed between the
+.B \-n
+and the
+.IR namelength. Currently, the only allowable
+values are 14 and 30.
+.B 30 is the default.
+.TP
+.BI \-i " inodecount"
+Specify the number of inodes for the filesystem.
+.TP
+.BI \-l " filename"
+Read the bad blocks list from
+.IR filename .
+The file has one bad block number per line. The count of bad blocks read
+is printed.
+.SH "EXIT CODES"
+The exit code returned by
+.B mkfs.minix
+is one of the following:
+.IP 0
+No errors
+.IP 8
+Operational error
+.IP 16
+Usage or syntax error
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR mkefs (8),
+.BR efsck (8),
+.BR reboot (8)
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
+.br
+Error code values by Rik Faith (faith@cs.unc.edu)
+.br
+Inode request feature by Scott Heavner (sdh@po.cwru.edu)
+.br
+Support for the file system valid flag by Dr. Wettstein
+(greg%wind.uucp@plains.nodak.edu)
diff --git a/disk-utils/mkfs.minix.c b/disk-utils/mkfs.minix.c
new file mode 100644
index 0000000000..b2dcf78963
--- /dev/null
+++ b/disk-utils/mkfs.minix.c
@@ -0,0 +1,533 @@
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 24.11.91 - time began. Used the fsck sources to get started.
+ *
+ * 25.11.91 - corrected some bugs. Added support for ".badblocks"
+ * The algorithm for ".badblocks" is a bit weird, but
+ * it should work. Oh, well.
+ *
+ * 25.01.92 - Added the -l option for getting the list of bad blocks
+ * out of a named file. (Dave Rivers, rivers@ponds.uucp)
+ *
+ * 28.02.92 - added %-information when using -c.
+ *
+ * 28.02.93 - added support for other namelengths than the original
+ * 14 characters so that I can test the new kernel routines..
+ *
+ * Sat Oct 9 11:48:31 1993, faith@cs.unc.edu: make exit status conform
+ * to that required by fsutil
+ *
+ * 31.10.93 - added inode request feature, for backup floppies: use
+ * 32 inodes, for a news partition use more.
+ * (Scott Heavner, sdh@po.cwru.edu)
+ *
+ * Mon Jan 3 11:08:49 1994, Dr. Wettstein (greg%wind.uucp@plains.nodak.edu).
+ * Added support for file system valid flag.
+ *
+ * 9.11.94 - added test to prevent overwrite of mounted fs adapted
+ * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
+ * program. (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * Usage: mkfs [-c] [-nXX] [-iXX] device size-in-blocks
+ * mkfs [-l filename ] device size-in-blocks
+ *
+ * -c for readablility checking (SLOW!)
+ * -l for getting a list of bad blocks from a file.
+ * -n for namelength (currently the kernel only uses 14 or 30)
+ * -i for number of inodes
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <mntent.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+#ifndef __GNUC__
+#error "needs gcc for the bitop-__asm__'s"
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define MINIX_ROOT_INO 1
+#define MINIX_BAD_INO 2
+
+#define TEST_BUFFER_BLOCKS 16
+#define MAX_GOOD_BLOCKS 512
+
+#define UPPER(size,n) ((size+((n)-1))/(n))
+#define INODE_SIZE (sizeof(struct minix_inode))
+#define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK)
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+
+#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
+
+static char * program_name = "mkfs";
+static char * device_name = NULL;
+static int DEV = -1;
+static long BLOCKS = 0;
+static int check = 0;
+static int badblocks = 0;
+static int namelen = 30; /* default (changed to 30, per Linus's
+ suggestion, Sun Nov 21 08:05:07 1993) */
+static int dirsize = 16;
+static int magic = MINIX_SUPER_MAGIC;
+
+static char root_block[BLOCK_SIZE] = "\0";
+
+static char * inode_buffer = NULL;
+#define Inode (((struct minix_inode *) inode_buffer)-1)
+static char super_block_buffer[BLOCK_SIZE];
+#define Super (*(struct minix_super_block *)super_block_buffer)
+#define INODES ((unsigned long)Super.s_ninodes)
+#define ZONES ((unsigned long)Super.s_nzones)
+#define IMAPS ((unsigned long)Super.s_imap_blocks)
+#define ZMAPS ((unsigned long)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
+#define ZONESIZE ((unsigned long)Super.s_log_zone_size)
+#define MAXSIZE ((unsigned long)Super.s_max_size)
+#define MAGIC (Super.s_magic)
+#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
+
+static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS];
+static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS];
+
+static unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+static int used_good_blocks = 0;
+static unsigned long req_nr_inodes = 0;
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+#define inode_in_use(x) (bit(inode_map,(x)))
+#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
+
+#define mark_inode(x) (setbit(inode_map,(x)))
+#define unmark_inode(x) (clrbit(inode_map,(x)))
+
+#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1))
+#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1))
+
+/*
+ * Volatile to let gcc know that this doesn't return. When trying
+ * to compile this under minix, volatile gives a warning, as
+ * exit() isn't defined as volatile under minix.
+ */
+volatile void fatal_error(const char * fmt_string,int status)
+{
+ fprintf(stderr,fmt_string,program_name,device_name);
+ exit(status);
+}
+
+#define usage() fatal_error("Usage: %s [-c | -l filename] [-nXX] [-iXX] /dev/name blocks\n",16)
+#define die(str) fatal_error("%s: " str "\n",8)
+
+/*
+ * Check to make certain that our new filesystem won't be created on
+ * an already mounted partition. Code adapted from mke2fs, Copyright
+ * (C) 1994 Theodore Ts'o. Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+ FILE * f;
+ struct mntent * mnt;
+
+ if ((f = setmntent (MOUNTED, "r")) == NULL)
+ return;
+ while ((mnt = getmntent (f)) != NULL)
+ if (strcmp (device_name, mnt->mnt_fsname) == 0)
+ break;
+ endmntent (f);
+ if (!mnt)
+ return;
+
+ die("%s is mounted; will not make a filesystem here!");
+}
+
+void write_tables(void)
+{
+ /* Mark the super block valid. */
+ Super.s_state |= MINIX_VALID_FS;
+ Super.s_state &= ~MINIX_ERROR_FS;
+
+ if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET))
+ die("seek failed in write_tables");
+ if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE))
+ die("unable to write super-block");
+ if (IMAPS*BLOCK_SIZE != write(DEV,inode_map,IMAPS*BLOCK_SIZE))
+ die("Unable to write inode map");
+ if (ZMAPS*BLOCK_SIZE != write(DEV,zone_map,ZMAPS*BLOCK_SIZE))
+ die("Unable to write zone map");
+ if (INODE_BUFFER_SIZE != write(DEV,inode_buffer,INODE_BUFFER_SIZE))
+ die("Unable to write inodes");
+}
+
+void write_block(int blk, char * buffer)
+{
+ if (blk*BLOCK_SIZE != lseek(DEV, blk*BLOCK_SIZE, SEEK_SET))
+ die("seek failed in write_block");
+ if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE))
+ die("write failed in write_block");
+}
+
+int get_free_block(void)
+{
+ int blk;
+
+ if (used_good_blocks+1 >= MAX_GOOD_BLOCKS)
+ die("too many bad blocks");
+ if (used_good_blocks)
+ blk = good_blocks_table[used_good_blocks-1]+1;
+ else
+ blk = FIRSTZONE;
+ while (blk < ZONES && zone_in_use(blk))
+ blk++;
+ if (blk >= ZONES)
+ die("not enough good blocks");
+ good_blocks_table[used_good_blocks] = blk;
+ used_good_blocks++;
+ return blk;
+}
+
+void mark_good_blocks(void)
+{
+ int blk;
+
+ for (blk=0 ; blk < used_good_blocks ; blk++)
+ mark_zone(good_blocks_table[blk]);
+}
+
+inline int next(int zone)
+{
+ if (!zone)
+ zone = FIRSTZONE-1;
+ while (++zone < ZONES)
+ if (zone_in_use(zone))
+ return zone;
+ return 0;
+}
+
+void make_bad_inode(void)
+{
+ struct minix_inode * inode = &Inode[MINIX_BAD_INO];
+ int i,j,zone;
+ int ind=0,dind=0;
+ unsigned short ind_block[BLOCK_SIZE>>1];
+ unsigned short dind_block[BLOCK_SIZE>>1];
+
+#define NEXT_BAD (zone = next(zone))
+
+ if (!badblocks)
+ return;
+ mark_inode(MINIX_BAD_INO);
+ inode->i_nlinks = 1;
+ inode->i_time = time(NULL);
+ inode->i_mode = S_IFREG + 0000;
+ inode->i_size = badblocks*BLOCK_SIZE;
+ zone = next(0);
+ for (i=0 ; i<7 ; i++) {
+ inode->i_zone[i] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ inode->i_zone[7] = ind = get_free_block();
+ memset(ind_block,0,BLOCK_SIZE);
+ for (i=0 ; i<512 ; i++) {
+ ind_block[i] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ inode->i_zone[8] = dind = get_free_block();
+ memset(dind_block,0,BLOCK_SIZE);
+ for (i=0 ; i<512 ; i++) {
+ write_block(ind,(char *) ind_block);
+ dind_block[i] = ind = get_free_block();
+ memset(ind_block,0,BLOCK_SIZE);
+ for (j=0 ; j<512 ; j++) {
+ ind_block[j] = zone;
+ if (!NEXT_BAD)
+ goto end_bad;
+ }
+ }
+ die("too many bad blocks");
+end_bad:
+ if (ind)
+ write_block(ind, (char *) ind_block);
+ if (dind)
+ write_block(dind, (char *) dind_block);
+}
+
+void make_root_inode(void)
+{
+ struct minix_inode * inode = &Inode[MINIX_ROOT_INO];
+
+ mark_inode(MINIX_ROOT_INO);
+ inode->i_zone[0] = get_free_block();
+ inode->i_nlinks = 2;
+ inode->i_time = time(NULL);
+ if (badblocks)
+ inode->i_size = 3*dirsize;
+ else {
+ root_block[2*dirsize] = '\0';
+ root_block[2*dirsize+1] = '\0';
+ inode->i_size = 2*dirsize;
+ }
+ inode->i_mode = S_IFDIR + 0755;
+ write_block(inode->i_zone[0],root_block);
+}
+
+void setup_tables(void)
+{
+ int i;
+
+ memset(inode_map,0xff,sizeof(inode_map));
+ memset(zone_map,0xff,sizeof(zone_map));
+ memset(super_block_buffer,0,BLOCK_SIZE);
+ MAGIC = magic;
+ ZONESIZE = 0;
+ MAXSIZE = (7+512+512*512)*1024;
+ ZONES = BLOCKS;
+/* some magic nrs: 1 inode / 3 blocks */
+ if ( req_nr_inodes == 0 )
+ INODES = BLOCKS/3;
+ else
+ INODES = req_nr_inodes;
+/* I don't want some off-by-one errors, so this hack... */
+ if ((INODES & 8191) > 8188)
+ INODES -= 5;
+ if ((INODES & 8191) < 10)
+ INODES -= 20;
+ IMAPS = UPPER(INODES,BITS_PER_BLOCK);
+ ZMAPS = 0;
+ while (ZMAPS != UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK))
+ ZMAPS = UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK);
+ FIRSTZONE = NORM_FIRSTZONE;
+ for (i = FIRSTZONE ; i<ZONES ; i++)
+ unmark_zone(i);
+ for (i = MINIX_ROOT_INO ; i<INODES ; i++)
+ unmark_inode(i);
+ inode_buffer = malloc(INODE_BUFFER_SIZE);
+ if (!inode_buffer)
+ die("Unable to allocate buffer for inodes");
+ memset(inode_buffer,0,INODE_BUFFER_SIZE);
+ printf("%d inodes\n",INODES);
+ printf("%d blocks\n",ZONES);
+ printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE);
+ printf("Zonesize=%d\n",BLOCK_SIZE<<ZONESIZE);
+ printf("Maxsize=%d\n\n",MAXSIZE);
+}
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable/writeable.
+ */
+long do_check(char * buffer, int try, unsigned int current_block)
+{
+ long got;
+
+ /* Seek to the correct loc. */
+ if (lseek(DEV, current_block * BLOCK_SIZE, SEEK_SET) !=
+ current_block * BLOCK_SIZE ) {
+ die("seek failed during testing of blocks");
+ }
+
+
+ /* Try the read */
+ got = read(DEV, buffer, try * BLOCK_SIZE);
+ if (got < 0) got = 0;
+ if (got & (BLOCK_SIZE - 1 )) {
+ printf("Weird values in do_check: probably bugs\n");
+ }
+ got /= BLOCK_SIZE;
+ return got;
+}
+
+static unsigned int currently_testing = 0;
+
+void alarm_intr(int alnum)
+{
+ if (currently_testing >= ZONES)
+ return;
+ signal(SIGALRM,alarm_intr);
+ alarm(5);
+ if (!currently_testing)
+ return;
+ printf("%d ...", currently_testing);
+ fflush(stdout);
+}
+
+void check_blocks(void)
+{
+ int try,got;
+ static char buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+ currently_testing=0;
+ signal(SIGALRM,alarm_intr);
+ alarm(5);
+ while (currently_testing < ZONES) {
+ if (lseek(DEV,currently_testing*BLOCK_SIZE,SEEK_SET) !=
+ currently_testing*BLOCK_SIZE)
+ die("seek failed in check_blocks");
+ try = TEST_BUFFER_BLOCKS;
+ if (currently_testing + try > ZONES)
+ try = ZONES-currently_testing;
+ got = do_check(buffer, try, currently_testing);
+ currently_testing += got;
+ if (got == try)
+ continue;
+ if (currently_testing < FIRSTZONE)
+ die("bad blocks before data-area: cannot make fs");
+ mark_zone(currently_testing);
+ badblocks++;
+ currently_testing++;
+ }
+ if (badblocks)
+ printf("%d bad block%s\n",badblocks,(badblocks>1)?"s":"");
+}
+
+void get_list_blocks(filename)
+char *filename;
+{
+ FILE *listfile;
+ unsigned long blockno;
+
+ listfile=fopen(filename,"r");
+ if(listfile == (FILE *)NULL) {
+ die("Can't open file of bad blocks");
+ }
+ while(!feof(listfile)) {
+ fscanf(listfile,"%d\n", &blockno);
+ mark_zone(blockno);
+ badblocks++;
+ }
+ if(badblocks) {
+ printf("%d bad block%s\n", badblocks, (badblocks>1)?"s":"");
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ int i;
+ char * tmp;
+ struct stat statbuf;
+ char * listfile = NULL;
+
+ if (argc && *argv)
+ program_name = *argv;
+ if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
+ die("bad inode size");
+ while (argc-- > 1) {
+ argv++;
+ if (argv[0][0] != '-')
+ if (device_name) {
+ BLOCKS = strtol(argv[0],&tmp,0);
+ if (*tmp) {
+ printf("strtol error: number of"
+ " blocks not specified");
+ usage();
+ }
+ } else
+ device_name = argv[0];
+ else {
+ if(argv[0][1] == 'l') {
+ listfile = argv[1];
+ argv++;
+ if (!(argc--))
+ usage();
+ } else {
+ if(argv[0][1] == 'i') {
+ req_nr_inodes
+ = (unsigned long)atol(argv[1]);
+ argv++;
+ if (!(argc--))
+ usage();
+ } else while (*(++argv[0])) {
+ switch (argv[0][0]) {
+ case 'c': check=1; break;
+ case 'n':
+ i = strtoul(argv[0]+1,&tmp,0);
+ if (*tmp)
+ usage();
+ argv[0][1] = '\0';
+ if (i == 14)
+ magic = MINIX_SUPER_MAGIC;
+ else if (i == 30)
+ magic = MINIX_SUPER_MAGIC2;
+ else
+ usage();
+ namelen = i;
+ dirsize = i+2;
+ break;
+ default: usage();
+ }
+ }
+ }
+ }
+ }
+ if (!device_name || BLOCKS<10 || BLOCKS > 65536) {
+ usage();
+ }
+ check_mount(); /* is it already mounted? */
+ tmp = root_block;
+ tmp[0] = 1;
+ tmp[1] = 0;
+ strcpy(tmp+2,".");
+ tmp += dirsize;
+ tmp[0] = 1;
+ tmp[1] = 0;
+ strcpy(tmp+2,"..");
+ tmp += dirsize;
+ tmp[0] = 2;
+ tmp[1] = 0;
+ strcpy(tmp+2,".badblocks");
+ DEV = open(device_name,O_RDWR );
+ if (DEV<0)
+ die("unable to open %s");
+ if (fstat(DEV,&statbuf)<0)
+ die("unable to stat %s");
+ if (!S_ISBLK(statbuf.st_mode))
+ check=0;
+ else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+ die("Will not try to make filesystem on '%s'");
+ setup_tables();
+ if (check)
+ check_blocks();
+ else if (listfile)
+ get_list_blocks(listfile);
+ make_root_inode();
+ make_bad_inode();
+ mark_good_blocks();
+ write_tables();
+ return 0;
+}
diff --git a/disk-utils/mkswap.8 b/disk-utils/mkswap.8
new file mode 100644
index 0000000000..2c02fbbe06
--- /dev/null
+++ b/disk-utils/mkswap.8
@@ -0,0 +1,86 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" Modified with suggestions from Linus, Mon Feb 1 21:40:49 1993
+.\" Modified with patches from Kai, Wed Jun 22 21:54:56 1994
+.\" Patches from jaggy@purplet.demon.co.uk (Mike Jagdis), Wed Feb 8 1995
+.\" Added comments from Nick Holloway, Sat Feb 11 1995, faith@cs.unc.edu
+.\" "
+.TH MKSWAP 8 "8 February 1995" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+mkswap \- set up a Linux swap device
+.SH SYNOPSIS
+.B "mkswap [ \-c ]"
+.IB device " [" size-in-blocks "]"
+.SH DESCRIPTION
+.B mkswap
+sets up a Linux swap area on a device (usually a disk partition).
+
+The
+.I device
+is usually of the following form:
+
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+The
+.I size-in-blocks
+parameter is the desired size of the file system, in blocks. This
+information is determined automatically by mkswap if it is omitted. Block
+counts are rounded down to pages of 4 kB each. Only block counts equal to
+or greater than 40 and equal to or less than 131072 are allowed. Block
+counts greater than 130752 are (silently) rounded down to 130752.
+
+As Nick Holloway explains, the actual maximum for each swap file/partition
+is:
+.RS
+(4096 - 10) * 8 * 4096 = 133890048 bytes = 130752 blocks = 127.6875 Mb
+.RE
+This is because a single page is used to hold the swap bitmap at the
+start of the partition, where each bit is a single 4K page. The reason
+for the -10, is that the signature is "SWAP-SPACE" -- 10 characters.
+
+.B mkswap
+can also set up swap files, although the file has to be created first. A
+sequence of commands similar to the following is reasonable for this
+purpose:
+
+.nf
+.RS
+# dd if=/dev/zero of=swapfile bs=1024 count=8192
+# mkswap swapfile 8192
+# sync
+# swapon swapfile
+.RE
+.fi
+
+Note that the regular file has to be created before running
+.B mkswap
+on the file, and that the file must not contain any holes (so, using
+.BR cp (1)
+to create the file is not acceptable).
+
+.SH OPTIONS
+.TP
+.B \-c
+Check the device for bad blocks before creating the file system. If any
+are found, the count is printed. This option is meant to be used for swap
+partitions
+.BR only ,
+and should
+.B not
+be used for regular files! To make sure that regular files do not contain
+bad blocks, the partition that contains the regular file should have been
+created with
+.BR "mkfs -c" .
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR mkfs (8),
+.BR fdisk (8)
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c
new file mode 100644
index 0000000000..bb4e2249e9
--- /dev/null
+++ b/disk-utils/mkswap.c
@@ -0,0 +1,212 @@
+/*
+ * mkswap.c - set up a linux swap device
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
+ *
+ * Usuage: mkswap [-c] device [size-in-blocks]
+ *
+ * -c for readablility checking (use it unless you are SURE!)
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
+ * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <linux/mm.h>
+
+#ifndef __GNUC__
+#error "needs gcc for the bitop-__asm__'s"
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define TEST_BUFFER_PAGES 8
+
+static char * program_name = "mkswap";
+static char * device_name = NULL;
+static int DEV = -1;
+static long PAGES = 0;
+static int check = 0;
+static int badpages = 0;
+
+static char signature_page[PAGE_SIZE];
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+/*
+ * Volatile to let gcc know that this doesn't return. When trying
+ * to compile this under minix, volatile gives a warning, as
+ * exit() isn't defined as volatile under minix.
+ */
+volatile void fatal_error(const char * fmt_string)
+{
+ fprintf(stderr,fmt_string,program_name,device_name);
+ exit(1);
+}
+
+#define usage() fatal_error("Usage: %s [-c] /dev/name [blocks]\n")
+#define die(str) fatal_error("%s: " str "\n")
+
+void check_blocks(void)
+{
+ unsigned int current_page;
+ int do_seek = 1;
+ static char buffer[PAGE_SIZE];
+
+ current_page = 0;
+ while (current_page < PAGES) {
+ if (!check) {
+ setbit(signature_page,current_page++);
+ continue;
+ }
+ if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) !=
+ current_page*PAGE_SIZE)
+ die("seek failed in check_blocks");
+ if (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) {
+ clrbit(signature_page,current_page++);
+ badpages++;
+ continue;
+ }
+ setbit(signature_page,current_page++);
+ }
+ if (badpages)
+ printf("%d bad page%s\n",badpages,(badpages>1)?"s":"");
+}
+
+static long valid_offset (int fd, int offset)
+{
+ char ch;
+
+ if (lseek (fd, offset, 0) < 0)
+ return 0;
+ if (read (fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+static int count_blocks (int fd)
+{
+ int high, low;
+
+ low = 0;
+ for (high = 1; valid_offset (fd, high); high *= 2)
+ low = high;
+ while (low < high - 1)
+ {
+ const int mid = (low + high) / 2;
+
+ if (valid_offset (fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ valid_offset (fd, 0);
+ return (low + 1);
+}
+
+static int get_size(const char *file)
+{
+ int fd;
+ int size;
+
+ fd = open(file, O_RDWR);
+ if (fd < 0) {
+ perror(file);
+ exit(1);
+ }
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ close(fd);
+ return (size * 512);
+ }
+
+ size = count_blocks(fd);
+ close(fd);
+ return size;
+}
+
+int main(int argc, char ** argv)
+{
+ char * tmp;
+ struct stat statbuf;
+ int goodpages;
+
+ memset(signature_page,0,PAGE_SIZE);
+ if (argc && *argv)
+ program_name = *argv;
+ while (argc-- > 1) {
+ argv++;
+ if (argv[0][0] != '-')
+ if (device_name) {
+ PAGES = strtol(argv[0],&tmp,0)>>2;
+ if (*tmp)
+ usage();
+ } else
+ device_name = argv[0];
+ else while (*++argv[0])
+ switch (argv[0][0]) {
+ case 'c': check=1; break;
+ default: usage();
+ }
+ }
+ if (device_name && !PAGES) {
+ PAGES = get_size(device_name) / PAGE_SIZE;
+ }
+ if (!device_name || PAGES<10)
+ usage();
+ if (PAGES > 32688) /* 130752 blocks */
+ PAGES=32688;
+#if 0
+ if (PAGES > 32768)
+ PAGES=32768;
+#endif
+ DEV = open(device_name,O_RDWR);
+ if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
+ perror(device_name);
+ exit(1);
+ }
+ if (!S_ISBLK(statbuf.st_mode))
+ check=0;
+ else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+ die("Will not try to make swapdevice on '%s'");
+ check_blocks();
+ if (!clrbit(signature_page,0))
+ die("fatal: first page unreadable");
+ goodpages = PAGES - badpages - 1;
+ if (!goodpages)
+ die("Unable to set up swap-space: unreadable");
+ printf("Setting up swapspace, size = %d bytes\n",goodpages*PAGE_SIZE);
+ strncpy(signature_page+PAGE_SIZE-10,"SWAP-SPACE",10);
+ if (lseek(DEV, 0, SEEK_SET))
+ die("unable to rewind swap-device");
+ if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE))
+ die("unable to write signature page");
+ return 0;
+}
diff --git a/disk-utils/setfdprm.8 b/disk-utils/setfdprm.8
new file mode 100644
index 0000000000..63143256d7
--- /dev/null
+++ b/disk-utils/setfdprm.8
@@ -0,0 +1,66 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH SETFDPRM 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+setfdprm \- sets user-provided floppy disk parameters
+.SH SYNOPSIS
+.B "setfdprm [ \-p ]"
+device name
+.br
+.B "setfdprm [ \-p ]"
+device size sectors heads tracks stretch gap rate spec1 fmt_gap
+.br
+.B "setfdprm [ \-c ]"
+device
+.br
+.B "setfdprm [ \-y ]"
+device
+.br
+.B "setfdprm [ \-n ]"
+device
+.SH DESCRIPTION
+.B setfdprm
+is a utility that can be used to load disk parameters into the
+auto-detecting floppy devices, to clear old parameter sets and to disable
+or enable diagnostic messages.
+
+Without any options,
+.B setfdprm
+loads the
+.I device
+(usually
+.I /dev/fd0
+or
+.IR /dev/fd1 )
+with a new parameter set with the
+.I name
+entry found in
+.I /etc/fdprm
+(usually named 360/360, etc.). These parameters stay in effect until the
+media is changed.
+.OPTIONS
+.TP
+.BI \-p " device name"
+Permanently loads a new parameter set for the specified auto-configuring
+floppy device for the configuration with
+.I name
+in
+.IR /etc/fdprm .
+Alternatively, the parameters can be given directly from the command line.
+.TP
+.BI \-c " device"
+Clears the parameter set of the specified auto-configuring floppy device.
+.TP
+.BI -y " device"
+Enables format detection messages for the specified auto-configuring floppy
+device.
+.TP
+.BI -n " device"
+Disables format detection messages for the specified auto-configuring
+floppy device.
+.SH BUGS
+This documentation is grossly incomplete.
+.SH FILES
+.I /etc/fdprm
+.SH AUTHOR
+Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
diff --git a/disk-utils/setfdprm.c b/disk-utils/setfdprm.c
new file mode 100644
index 0000000000..bd98f5a2f1
--- /dev/null
+++ b/disk-utils/setfdprm.c
@@ -0,0 +1,149 @@
+/* setfdprm.c - Sets user-provided floppy disk parameters, re-activates
+ autodetection and switches diagnostic messages. */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/fd.h>
+
+#define FDPRMFILE "/etc/fdprm"
+#define MAXLINE 200
+
+
+static int convert(char *arg)
+{
+ long result;
+ char *end;
+
+ result = strtol(arg,&end,0);
+ if (!*end) return (int) result;
+ fprintf(stderr,"Invalid number: %s\n",arg);
+ exit(1);
+}
+
+
+static void cmd_without_param(int cmd,int fd)
+{
+ if (ioctl(fd,cmd,NULL) >= 0) exit(0);
+ perror("ioctl");
+ exit(1);
+}
+
+
+static void set_params(int cmd,int fd,char **params)
+{
+ struct floppy_struct ft;
+
+ ft.size = convert(params[0]);
+ ft.sect = convert(params[1]);
+ ft.head = convert(params[2]);
+ ft.track = convert(params[3]);
+ ft.stretch = convert(params[4]);
+ ft.gap = convert(params[5]);
+ ft.rate = convert(params[6]);
+ ft.spec1 = convert(params[7]);
+ ft.fmt_gap = convert(params[8]);
+ ft.name = NULL;
+ if (ioctl(fd,cmd,&ft) >= 0) exit(0);
+ perror("ioctl");
+ exit(1);
+}
+
+
+static void find_params(int cmd,int fd,char *name)
+{
+ FILE *file;
+ char line[MAXLINE+2],this[MAXLINE+2],param[9][MAXLINE+2];
+ char *params[9],*start;
+ int count;
+
+ if ((file = fopen(FDPRMFILE,"r")) == NULL) {
+ perror(FDPRMFILE);
+ exit(1);
+ }
+ while (fgets(line,MAXLINE,file)) {
+ for (start = line; *start == ' ' || *start == '\t'; start++);
+ if (*start && *start != '\n' && *start != '#') {
+ if (sscanf(start,"%s %s %s %s %s %s %s %s %s %s",this,param[0],
+ param[1],param[2],param[3],param[4],param[5],param[6],param[7],
+ param[8]) != 10) {
+ fprintf(stderr,"Syntax error: '%s'\n",line);
+ exit(1);
+ }
+ if (!strcmp(this,name)) {
+ for (count = 0; count < 9; count++)
+ params[count] = param[count];
+ set_params(cmd,fd,params);
+ }
+ }
+ }
+ fprintf(stderr,"No such parameter set: '%s'\n",name);
+ exit(1);
+}
+
+
+static void usage(char *name)
+{
+ char *this;
+
+ if (this = strrchr(name,'/')) name = this+1;
+ fprintf(stderr,"usage: %s [ -p ] dev name\n",name);
+ fprintf(stderr," %s [ -p ] dev size sect heads tracks stretch \
+gap rate spec1 fmt_gap\n",name);
+#ifdef FDMEDCNG
+ fprintf(stderr," %s [ -c | -y | -n | -d ] dev\n",name);
+#else
+ fprintf(stderr," %s [ -c | -y | -n ] dev\n",name);
+#endif
+ exit(1);
+}
+
+
+main(int argc,char **argv)
+{
+ int cmd,fd;
+ char *name;
+
+ name = argv[0];
+ if (argc < 3) usage(name);
+ cmd = FDSETPRM;
+ if (*argv[1] == '-') {
+ switch (argv[1][1]) {
+ case 'c':
+ cmd = FDCLRPRM;
+ break;
+ case 'p':
+ cmd = FDDEFPRM;
+ break;
+ case 'y':
+ cmd = FDMSGON;
+ break;
+ case 'n':
+ cmd = FDMSGOFF;
+ break;
+#ifdef FDMEDCNG
+ case 'd':
+ cmd = FDMEDCNG;
+ break;
+#endif
+ default:
+ usage(name);
+ }
+ argc--;
+ argv++;
+ }
+ if ((fd = open(argv[1],3)) < 0) { /* 3 == no access at all */
+ perror(argv[1]);
+ exit(1);
+ }
+ if (cmd != FDSETPRM && cmd != FDDEFPRM) {
+ if (argc != 2) usage(name);
+ cmd_without_param(cmd,fd);
+ }
+ if (argc != 11 && argc != 3) usage(name);
+ if (argc == 11) set_params(cmd,fd,&argv[2]);
+ else find_params(cmd,fd,argv[2]);
+}