diff options
Diffstat (limited to 'disk-utils')
| -rw-r--r-- | disk-utils/Makefile | 57 | ||||
| -rw-r--r-- | disk-utils/README.bootutils-0.1 | 104 | ||||
| -rw-r--r-- | disk-utils/README.cfdisk | 45 | ||||
| -rw-r--r-- | disk-utils/README.fdisk | 577 | ||||
| -rw-r--r-- | disk-utils/cfdisk.8 | 407 | ||||
| -rw-r--r-- | disk-utils/cfdisk.c | 2330 | ||||
| -rw-r--r-- | disk-utils/fdformat.8 | 59 | ||||
| -rw-r--r-- | disk-utils/fdformat.c | 109 | ||||
| -rw-r--r-- | disk-utils/fdisk.8 | 166 | ||||
| -rw-r--r-- | disk-utils/fdisk.c | 1339 | ||||
| -rw-r--r-- | disk-utils/fdprm | 26 | ||||
| -rw-r--r-- | disk-utils/frag.8 | 47 | ||||
| -rw-r--r-- | disk-utils/frag.c | 311 | ||||
| -rw-r--r-- | disk-utils/fsck.minix.8 | 125 | ||||
| -rw-r--r-- | disk-utils/fsck.minix.c | 862 | ||||
| -rw-r--r-- | disk-utils/llseek.c | 84 | ||||
| -rw-r--r-- | disk-utils/mkfs.8 | 133 | ||||
| -rw-r--r-- | disk-utils/mkfs.c | 297 | ||||
| -rw-r--r-- | disk-utils/mkfs.minix.8 | 88 | ||||
| -rw-r--r-- | disk-utils/mkfs.minix.c | 533 | ||||
| -rw-r--r-- | disk-utils/mkswap.8 | 86 | ||||
| -rw-r--r-- | disk-utils/mkswap.c | 212 | ||||
| -rw-r--r-- | disk-utils/setfdprm.8 | 66 | ||||
| -rw-r--r-- | disk-utils/setfdprm.c | 149 |
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) ¶m) < 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,¤t_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]); +} |
