From f46af50517565ed59e9c7e537a202c302d3d6367 Mon Sep 17 00:00:00 2001 From: "Jordan K. Hubbard" Date: Wed, 6 Nov 1996 14:08:39 +0000 Subject: [PATCH] Allow fdisk to be driven from a configuration file, making a 3rd-party utility for front-ending its operation more of a possibility. 2.2-RELEASE candiate. Closes PR#1960 Submitted-By: Darryl Okahata --- sbin/fdisk/fdisk.8 | 272 +++++++++++++++++--- sbin/fdisk/fdisk.c | 550 ++++++++++++++++++++++++++++++++++++++-- sbin/i386/fdisk/fdisk.8 | 272 +++++++++++++++++--- sbin/i386/fdisk/fdisk.c | 550 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 1542 insertions(+), 102 deletions(-) diff --git a/sbin/fdisk/fdisk.8 b/sbin/fdisk/fdisk.8 index 36780e787f7c..a43a85a16f87 100644 --- a/sbin/fdisk/fdisk.8 +++ b/sbin/fdisk/fdisk.8 @@ -1,4 +1,4 @@ -.Dd April 4, 1993 +.Dd October 4, 1996 .Dt FDISK 8 .\".Os BSD 4 .Sh NAME @@ -10,32 +10,14 @@ .Op Fl u .Op Fl a .Op Fl 0123 -.Op disk +.Op Ar disk .Bl -tag -width time -.It Fl u -Is used for updating (editing) sector 0 of the disk. -.It Fl i -Initializes sector 0 of the disk. This implies -.Fl u . -.It Fl a -Change the active partition only. -.It Fl 0123 -Operate on a single fdisk entry only. -.El -.Pp -The final disk name can be provided as a -.Sq bare -disk name only, e.g. -.Ql sd0 , -or as a fully qualified device node under -.Pa /dev . -If omitted, the disks -.Ql wd0 , -.Ql sd0 , -and -.Ql od0 -are being searched in that order, until one is -being found responding. +.Nm fdisk +.Op Fl f Ar configfile +.Op Fl i +.Op Fl v +.Op Fl t +.Op Ar disk .Sh PROLOGUE In order for the BIOS to boot the kernel, certain conventions must be adhered to. @@ -60,9 +42,84 @@ The DOS program can be used to divide space on the disk into partitions and set one .Em active. .Sh DESCRIPTION +.Pp The FreeBSD program .Nm -serves a similar purpose to the DOS program. +serves a similar purpose to the DOS program. The first form is used to +display partition information or to interactively edit the partition +table. The second is used to write a partition table using a +.Ar configfile +and is designed to be used by other scripts/programs. +.Pp +Options are: +.It Fl u +Is used for updating (editing) sector 0 of the disk. Ignored if +.Fl f +is given. +.It Fl i +Initializes sector 0 of the disk. This implies +.Fl u , +unless +.Fl f +is given. +.It Fl a +Change the active partition only. Ignored if +.Fl f +is given. +.It Fl 0123 +Operate on a single fdisk entry only. Ignored if +.Fl f +is given. +.It Fl f Ar configfile +Set partition values using the file +.Ar configfile . +The +.Ar configfile +always modifies existing partitions, unless +.Fl i +is also given, in which case all existing partitions are deleted (marked +as "unused") before the +.Ar configfile +is read. The +.Ar configfile +can be "-", in which case +.Ar stdin +is read. See +.Em CONFIGURATION FILE , +below, for file syntax. +.Pp +.Em WARNING: +when +.Fl f +is used, you are not asked if you really want to write the partition +table (as you are in the interactive mode). Use with caution! +.It Fl t +Test mode; do not write partition values. Generally used with the +.Fl f +option to see what would be written to the partition table. Implies +.Fl v . +.It Fl v +Be verbose. When +.Fl f +is used, +.Nm +prints out the partition table that is written to the disk. +.El +.Pp +The final disk name can be provided as a +.Sq bare +disk name only, e.g. +.Ql sd0 , +or as a fully qualified device node under +.Pa /dev . +If omitted, the disks +.Ql wd0 , +.Ql sd0 , +and +.Ql od0 +are being searched in that order, until one is +being found responding. +.Pp When called with no arguments, it prints the sector 0 partition table. An example follows: @@ -119,8 +176,11 @@ The flags .Fl i or .Fl u -are used to indicate that the partition data is to be updated. -The +are used to indicate that the partition data is to be updated, unless the +.Fl f +option is used. If the +.Fl f +option is not used, the .Nm program will enter a conversational mode. This mode is designed not to change any data unless you explicitly tell it to. @@ -188,11 +248,151 @@ A number of decisions made later may assume this. Editing an existing partition will most likely cause you to lose all the data in that partition. .Pp -You should run this program interactively once or twice to see how it works. -This is completely safe as long as you answer the last question in the negative. -There are subtleties -that the program detects -that are not fully explained in this manual page. +You should run this program interactively once or twice to see how it +works. This is completely safe as long as you answer the last question +in the negative. There are subtleties that the program detects that are +not fully explained in this manual page. +.Sh CONFIGURATION FILE +.Pp +When the +.Fl f +option is given, a disk's partition table can be written using values +from a +.Ar configfile . +The syntax of this file is very simple. Each line is either a comment or +a specification, and whitespace (except for newlines) are ignored: +.Bl -tag -width Ds +.It Xo +.Ic # +.No Ar comment ... +.Xc +Lines beginning with a "#" are comments and are ignored. +.It Xo +.Ic g +.No Ar spec1 +.No Ar spec2 +.No Ar spec3 +.Xc +Set the BIOS geometry used in partition calculations. There must be +three values specfied, with a letter preceding each number: +.Bl -tag -width Ds +.Sm off +.It Cm c No Ar num +.Sm on +Set the number of cylinders to +.Ar num . +.Sm off +.It Cm h No Ar num +.Sm on +Set the number of heads to +.Ar num . +.Sm off +.It Cm s No Ar num +.Sm on +Set the number of sectors/track to +.Ar num . +.El +.Pp +These specs can occur in any order, as the leading letter determines +which value is which; however, all three must be specified. +.Pp +This line must occur before any lines that specify partition +information. +.Pp +It is an error if the following is not true: +.Pp +.nf + 1 <= number of cylinders + 1 <= number of heads <= 256 + 1 <= number of sectors/track < 64 +.fi +.Pp +The number of cylinders should be less than or equal to 1024, but this +is not enforced, although a warning will be output. Note that bootable +FreeBSD partitions (the "/" filesystem) must lie completely within the +first 1024 cylinders; if this is not true, booting may fail. +Non-bootable partitions do not have this restriction. +.Pp +Example (all of these are equivalent), for a disk with 1019 cylinders, +39 heads, and 63 sectors: +.Pp +.nf + g c1019 h39 s63 + g h39 c1019 s63 + g s63 h39 c1019 +.fi +.It Xo +.Ic p +.No Ar partition +.No Ar type +.No Ar start +.No Ar length +.Xc +Set the partition given by +.Ar partition +(0-3) to type +.Ar type , +starting at sector +.Ar start +for +.Ar length +sectors. +.Pp +Only those partitions explicitly mentioned by these lines are modified; +any partition not referenced by a "p" line will not be modified. +However, if an invalid partition table is present, or the +.Fl i +option is specified, all existing partition entries will be cleared +(marked as unused), and these "p" lines will have to be used to +explicitly set partition information. If multiple partitions need to be +set, multiple "p" lines must be specified; one for each partition. +.Pp +These partition lines must occur after any geometry specification lines, +if one is present. +.Pp +The +.Ar type +is 165 for FreeBSD partitions. Specifying a partition type of zero is +the same as clearing the partition and marking it as unused; however, +dummy values (such as "0") must still be specified for +.Ar start +and +.Ar length . +.Pp +Note: the start offset will be rounded upwards to a head boundary if +necessary, and the end offset will be rounded downwards to a cylinder +boundary if necessary. +.Pp +Example: to clear partition 3 and mark it as unused: +.Pp +.nf + p 3 0 0 0 +.fi +.Pp +Example: to set partition 0 to a FreeBSD partition, starting at sector 1 +for 2503871 sectors (note: these numbers will be rounded upwards and +downwards to correspond to head and cylinder boundaries): +.Pp +.nf + p 0 165 1 2503871 +.fi +.It Xo +.Ic a +.No Ar partition +.Xc +Make +.Ar partition +the active partition. Can occur anywhere in the config file, but only +one must be present. +.Pp +Example: to make partition 0 the active partition: +.Pp +.nf + a 0 +.fi + +.El +.Pp .Sh SEE ALSO .Xr disklabel 8 .Sh BUGS @@ -203,3 +403,7 @@ Throughout this man page, the term is used where it should actually be .Sq slice , in order to conform with the terms used elsewhere. +.Pp +You cannot use this command to completely dedicate a disk to FreeBSD. The +.Xr disklabel 8 +command must be used for this. diff --git a/sbin/fdisk/fdisk.c b/sbin/fdisk/fdisk.c index 13418c896a8e..b89d86571681 100644 --- a/sbin/fdisk/fdisk.c +++ b/sbin/fdisk/fdisk.c @@ -90,9 +90,31 @@ int dos_cylsecs; static int partition = -1; +#define MAX_ARGS 10 + +static int current_line_number; + +static int geom_processed = 0; +static int part_processed = 0; +static int active_processed = 0; + + +typedef struct cmd { + char cmd; + int n_args; + struct arg { + char argtype; + int arg_val; + } args[MAX_ARGS]; +} CMD; + + static int a_flag = 0; /* set active partition */ static int i_flag = 0; /* replace partition data */ static int u_flag = 0; /* update partition data */ +static int t_flag = 0; /* test only, if f_flag is given */ +static char *f_flag = NULL; /* Read config info from file */ +static int v_flag = 0; /* Be verbose */ static unsigned char bootcode[] = { 0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf, @@ -179,6 +201,7 @@ struct part_type static void print_s0(int which); static void print_part(int i); static void init_sector0(unsigned long start); +static void init_boot(void); static void change_part(int i); static void print_params(); static void change_active(int which); @@ -194,6 +217,8 @@ static int write_s0(); static int ok(char *str); static int decimal(char *str, int *num, int deflt); static char *get_type(int type); +static int read_config(char *config_file); +static void reset_boot(void); #if 0 static int hex(char *str, int *num, int deflt); static int string(char *str, char **ans); @@ -231,11 +256,37 @@ main(int argc, char *argv[]) case 'a': a_flag = 1; break; + case 'f': + if (*token) + { + f_flag = token; + token = ""; + } + else + { + if (argc == 1) + { + goto usage; + } + --argc; + f_flag = *++argv; + } + /* + * u_flag is needed, because we're + * writing to the disk. + */ + u_flag = 1; + break; case 'i': i_flag = 1; case 'u': u_flag = 1; break; + case 't': + t_flag = 1; + case 'v': + v_flag = 1; + break; default: goto usage; } @@ -281,41 +332,75 @@ main(int argc, char *argv[]) } printf("******* Working on device %s *******\n",disk); - if(u_flag) + + if (f_flag) { - get_params_to_use(); + if (read_s0() || i_flag) + { + reset_boot(); + } + + if (!read_config(f_flag)) + { + exit(1); + } + if (v_flag) + { + print_s0(-1); + } + if (!t_flag) + { + write_s0(); + } } else { + if(u_flag) + { + get_params_to_use(); + } + else + { print_params(); - } + } - if (read_s0()) + if (read_s0()) init_sector0(1); - printf("Warning: BIOS sector numbering starts with sector 1\n"); - printf("Information from DOS bootblock is:\n"); - if (partition == -1) + printf("Warning: BIOS sector numbering starts with sector 1\n"); + printf("Information from DOS bootblock is:\n"); + if (partition == -1) for (i = 0; i < NDOSPART; i++) - change_part(i); - else + change_part(i); + else change_part(partition); - if (u_flag || a_flag) + if (u_flag || a_flag) change_active(partition); - if (u_flag || a_flag) { - printf("\nWe haven't changed the partition table yet. "); - printf("This is your last chance.\n"); + if (u_flag || a_flag) { + if (!t_flag) + { + printf("\nWe haven't changed the partition table yet. "); + printf("This is your last chance.\n"); + } print_s0(-1); - if (ok("Should we write new partition table?")) + if (!t_flag) + { + if (ok("Should we write new partition table?")) write_s0(); + } + else + { + printf("\n-t flag specified -- partition table not written.\n"); + } + } } exit(0); usage: - printf("fdisk {-a|-i|-u} [-{0,1,2,3}] [disk]\n"); + printf("fdisk {-a|-i|-u} [-f [-t] [-v]] [-{0,1,2,3}] [disk]\n"); return(1); } @@ -359,14 +444,22 @@ struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i; ,partp->dp_ehd); } + +static void +init_boot(void) +{ + memcpy(mboot.bootinst, bootcode, sizeof(bootcode)); + mboot.signature = BOOT_MAGIC; +} + + static void init_sector0(unsigned long start) { struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]); unsigned long size = disksecs - start; - memcpy(mboot.bootinst, bootcode, sizeof(bootcode)); - mboot.signature = BOOT_MAGIC; + init_boot(); partp->dp_typ = DOSPTYP_386BSD; partp->dp_flag = ACTIVE; @@ -490,6 +583,7 @@ get_params_to_use() } } + /***********************************************\ * Change real numbers into strange dos numbers * \***********************************************/ @@ -779,3 +873,425 @@ get_type(int type) } return("unknown"); } + + +static void +parse_config_line(line, command) + char *line; + CMD *command; +{ + char *cp, *end; + + cp = line; + while (1) /* dirty trick used to insure one exit point for this + function */ + { + memset(command, 0, sizeof(*command)); + + while (isspace(*cp)) ++cp; + if (*cp == '\0' || *cp == '#') + { + break; + } + command->cmd = *cp++; + + /* + * Parse args + */ + while (1) + { + while (isspace(*cp)) ++cp; + if (*cp == '#') + { + break; /* found comment */ + } + if (isalpha(*cp)) + { + command->args[command->n_args].argtype = *cp++; + } + if (!isdigit(*cp)) + { + break; /* assume end of line */ + } + end = NULL; + command->args[command->n_args].arg_val = strtol(cp, &end, 0); + if (cp == end) + { + break; /* couldn't parse number */ + } + cp = end; + command->n_args++; + } + break; + } +} + + +static int +process_geometry(command) + CMD *command; +{ + int status = 1, i; + + while (1) + { + geom_processed = 1; + if (part_processed) + { + fprintf(stderr, + "%s: ERROR line %d: the geometry specification line must occur before\n\ + all partition specifications.\n", + name, current_line_number); + status = 0; + break; + } + if (command->n_args != 3) + { + fprintf(stderr, + "%s: ERROR line %d: incorrect number of geometry args\n", + name, current_line_number); + status = 0; + break; + } + dos_cyls = -1; + dos_heads = -1; + dos_sectors = -1; + for (i = 0; i < 3; ++i) + { + switch (command->args[i].argtype) + { + case 'c': + dos_cyls = command->args[i].arg_val; + break; + case 'h': + dos_heads = command->args[i].arg_val; + break; + case 's': + dos_sectors = command->args[i].arg_val; + break; + default: + fprintf(stderr, + "%s: ERROR line %d: unknown geometry arg type: '%c' (0x%02x)\n", + name, current_line_number, command->args[i].argtype, + command->args[i].argtype); + status = 0; + break; + } + } + if (status == 0) + { + break; + } + + dos_cylsecs = dos_heads * dos_sectors; + + /* + * Do sanity checks on parameter values + */ + if (dos_cyls < 0) + { + fprintf(stderr, + "%s: ERROR line %d: number of cylinders not specified\n", + name, current_line_number); + status = 0; + } + if (dos_cyls == 0 || dos_cyls > 1024) + { + fprintf(stderr, + "%s: WARNING line %d: number of cylinders (%d) may be out-of-range\n\ + (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\ + is dedicated to FreeBSD).\n", + name, current_line_number, dos_cyls); + } + + if (dos_heads < 0) + { + fprintf(stderr, + "%s: ERROR line %d: number of heads not specified\n", + name, current_line_number); + status = 0; + } + else if (dos_heads < 1 || dos_heads > 256) + { + fprintf(stderr, + "%s: ERROR line %d: number of heads must be within (1-256)\n", + name, current_line_number); + status = 0; + } + + if (dos_sectors < 0) + { + fprintf(stderr, "%s: ERROR line %d: number of sectors not specified\n", + name, current_line_number); + status = 0; + } + else if (dos_sectors < 1 || dos_sectors > 63) + { + fprintf(stderr, + "%s: ERROR line %d: number of sectors must be within (1-63)\n", + name, current_line_number); + status = 0; + } + + break; + } + return (status); +} + + +static int +process_partition(command) + CMD *command; +{ + int status = 0, partition; + unsigned long chunks, adj_size, max_end; + struct dos_partition *partp; + + while (1) + { + part_processed = 1; + if (command->n_args != 4) + { + fprintf(stderr, + "%s: ERROR line %d: incorrect number of partition args\n", + name, current_line_number); + break; + } + partition = command->args[0].arg_val; + if (partition < 0 || partition > 3) + { + fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n", + name, current_line_number, partition); + break; + } + partp = ((struct dos_partition *) &mboot.parts) + partition; + bzero((char *)partp, sizeof (struct dos_partition)); + partp->dp_typ = command->args[1].arg_val; + partp->dp_start = command->args[2].arg_val; + partp->dp_size = command->args[3].arg_val; + max_end = partp->dp_start + partp->dp_size; + + if (partp->dp_typ == 0) + { + /* + * Get out, the partition is marked as unused. + */ + /* + * Insure that it's unused. + */ + bzero((char *)partp, sizeof (struct dos_partition)); + status = 1; + break; + } + + /* + * Adjust start upwards, if necessary, to fall on an head boundary. + */ + if (partp->dp_start % dos_sectors != 0) + { + adj_size = + (partp->dp_start / dos_sectors + 1) * dos_sectors; + if (adj_size > max_end) + { + /* + * Can't go past end of partition + */ + fprintf(stderr, + "%s: ERROR line %d: unable to adjust start of partition %d to fall on\n\ + a cylinder boundary.\n", + name, current_line_number, partition); + break; + } + fprintf(stderr, + "%s: WARNING: adjusting start offset of partition '%d' from %d\n\ + to %d, to round to an head boundary.\n", + name, partition, partp->dp_start, adj_size); + partp->dp_start = adj_size; + } + + /* + * Adjust size downwards, if necessary, to fall on a cylinder + * boundary. + */ + chunks = + ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs; + adj_size = chunks - partp->dp_start; + if (adj_size != partp->dp_size) + { + fprintf(stderr, + "%s: WARNING: adjusting size of partition '%d' from %d to %d,\n\ + to round to a cylinder boundary.\n", + name, partition, partp->dp_size, adj_size); + if (chunks > 0) + { + partp->dp_size = adj_size; + } + else + { + partp->dp_size = 0; + } + } + if (partp->dp_size < 1) + { + fprintf(stderr, + "%s: ERROR line %d: size for partition '%d' is zero.\n", + name, current_line_number, partition); + break; + } + + dos(partp->dp_start, partp->dp_size, + &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd); + dos(partp->dp_start+partp->dp_size - 1, partp->dp_size, + &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd); + status = 1; + break; + } + return (status); +} + + +static int +process_active(command) + CMD *command; +{ + int status = 0, partition, i; + struct dos_partition *partp; + + while (1) + { + active_processed = 1; + if (command->n_args != 1) + { + fprintf(stderr, + "%s: ERROR line %d: incorrect number of active args\n", + name, current_line_number); + status = 0; + break; + } + partition = command->args[0].arg_val; + if (partition < 0 || partition > 3) + { + fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n", + name, current_line_number, partition); + break; + } + /* + * Reset active partition + */ + partp = ((struct dos_partition *) &mboot.parts); + for (i = 0; i < NDOSPART; i++) + partp[i].dp_flag = 0; + partp[partition].dp_flag = ACTIVE; + + status = 1; + break; + } + return (status); +} + + +static int +process_line(line) + char *line; +{ + CMD command; + int status = 1; + + while (1) + { + parse_config_line(line, &command); + switch (command.cmd) + { + case 0: + /* + * Comment or blank line + */ + break; + case 'g': + /* + * Set geometry + */ + status = process_geometry(&command); + break; + case 'p': + status = process_partition(&command); + break; + case 'a': + status = process_active(&command); + break; + default: + status = 0; + break; + } + break; + } + return (status); +} + + +static int +read_config(config_file) + char *config_file; +{ + FILE *fp = NULL; + int status = 1; + char buf[1010]; + + while (1) /* dirty trick used to insure one exit point for this + function */ + { + if (strcmp(config_file, "-") != 0) + { + /* + * We're not reading from stdin + */ + if ((fp = fopen(config_file, "r")) == NULL) + { + status = 0; + break; + } + } + else + { + fp = stdin; + } + current_line_number = 0; + while (!feof(fp)) + { + if (fgets(buf, sizeof(buf), fp) == NULL) + { + break; + } + ++current_line_number; + status = process_line(buf); + if (status == 0) + { + break; + } + } + break; + } + if (fp) + { + /* + * It doesn't matter if we're reading from stdin, as we've reached EOF + */ + fclose(fp); + } + return (status); +} + + +static void +reset_boot(void) +{ + int i; + struct dos_partition *partp; + + init_boot(); + for (i = 0; i < 4; ++i) + { + partp = ((struct dos_partition *) &mboot.parts) + i; + bzero((char *)partp, sizeof (struct dos_partition)); + } +} diff --git a/sbin/i386/fdisk/fdisk.8 b/sbin/i386/fdisk/fdisk.8 index 36780e787f7c..a43a85a16f87 100644 --- a/sbin/i386/fdisk/fdisk.8 +++ b/sbin/i386/fdisk/fdisk.8 @@ -1,4 +1,4 @@ -.Dd April 4, 1993 +.Dd October 4, 1996 .Dt FDISK 8 .\".Os BSD 4 .Sh NAME @@ -10,32 +10,14 @@ .Op Fl u .Op Fl a .Op Fl 0123 -.Op disk +.Op Ar disk .Bl -tag -width time -.It Fl u -Is used for updating (editing) sector 0 of the disk. -.It Fl i -Initializes sector 0 of the disk. This implies -.Fl u . -.It Fl a -Change the active partition only. -.It Fl 0123 -Operate on a single fdisk entry only. -.El -.Pp -The final disk name can be provided as a -.Sq bare -disk name only, e.g. -.Ql sd0 , -or as a fully qualified device node under -.Pa /dev . -If omitted, the disks -.Ql wd0 , -.Ql sd0 , -and -.Ql od0 -are being searched in that order, until one is -being found responding. +.Nm fdisk +.Op Fl f Ar configfile +.Op Fl i +.Op Fl v +.Op Fl t +.Op Ar disk .Sh PROLOGUE In order for the BIOS to boot the kernel, certain conventions must be adhered to. @@ -60,9 +42,84 @@ The DOS program can be used to divide space on the disk into partitions and set one .Em active. .Sh DESCRIPTION +.Pp The FreeBSD program .Nm -serves a similar purpose to the DOS program. +serves a similar purpose to the DOS program. The first form is used to +display partition information or to interactively edit the partition +table. The second is used to write a partition table using a +.Ar configfile +and is designed to be used by other scripts/programs. +.Pp +Options are: +.It Fl u +Is used for updating (editing) sector 0 of the disk. Ignored if +.Fl f +is given. +.It Fl i +Initializes sector 0 of the disk. This implies +.Fl u , +unless +.Fl f +is given. +.It Fl a +Change the active partition only. Ignored if +.Fl f +is given. +.It Fl 0123 +Operate on a single fdisk entry only. Ignored if +.Fl f +is given. +.It Fl f Ar configfile +Set partition values using the file +.Ar configfile . +The +.Ar configfile +always modifies existing partitions, unless +.Fl i +is also given, in which case all existing partitions are deleted (marked +as "unused") before the +.Ar configfile +is read. The +.Ar configfile +can be "-", in which case +.Ar stdin +is read. See +.Em CONFIGURATION FILE , +below, for file syntax. +.Pp +.Em WARNING: +when +.Fl f +is used, you are not asked if you really want to write the partition +table (as you are in the interactive mode). Use with caution! +.It Fl t +Test mode; do not write partition values. Generally used with the +.Fl f +option to see what would be written to the partition table. Implies +.Fl v . +.It Fl v +Be verbose. When +.Fl f +is used, +.Nm +prints out the partition table that is written to the disk. +.El +.Pp +The final disk name can be provided as a +.Sq bare +disk name only, e.g. +.Ql sd0 , +or as a fully qualified device node under +.Pa /dev . +If omitted, the disks +.Ql wd0 , +.Ql sd0 , +and +.Ql od0 +are being searched in that order, until one is +being found responding. +.Pp When called with no arguments, it prints the sector 0 partition table. An example follows: @@ -119,8 +176,11 @@ The flags .Fl i or .Fl u -are used to indicate that the partition data is to be updated. -The +are used to indicate that the partition data is to be updated, unless the +.Fl f +option is used. If the +.Fl f +option is not used, the .Nm program will enter a conversational mode. This mode is designed not to change any data unless you explicitly tell it to. @@ -188,11 +248,151 @@ A number of decisions made later may assume this. Editing an existing partition will most likely cause you to lose all the data in that partition. .Pp -You should run this program interactively once or twice to see how it works. -This is completely safe as long as you answer the last question in the negative. -There are subtleties -that the program detects -that are not fully explained in this manual page. +You should run this program interactively once or twice to see how it +works. This is completely safe as long as you answer the last question +in the negative. There are subtleties that the program detects that are +not fully explained in this manual page. +.Sh CONFIGURATION FILE +.Pp +When the +.Fl f +option is given, a disk's partition table can be written using values +from a +.Ar configfile . +The syntax of this file is very simple. Each line is either a comment or +a specification, and whitespace (except for newlines) are ignored: +.Bl -tag -width Ds +.It Xo +.Ic # +.No Ar comment ... +.Xc +Lines beginning with a "#" are comments and are ignored. +.It Xo +.Ic g +.No Ar spec1 +.No Ar spec2 +.No Ar spec3 +.Xc +Set the BIOS geometry used in partition calculations. There must be +three values specfied, with a letter preceding each number: +.Bl -tag -width Ds +.Sm off +.It Cm c No Ar num +.Sm on +Set the number of cylinders to +.Ar num . +.Sm off +.It Cm h No Ar num +.Sm on +Set the number of heads to +.Ar num . +.Sm off +.It Cm s No Ar num +.Sm on +Set the number of sectors/track to +.Ar num . +.El +.Pp +These specs can occur in any order, as the leading letter determines +which value is which; however, all three must be specified. +.Pp +This line must occur before any lines that specify partition +information. +.Pp +It is an error if the following is not true: +.Pp +.nf + 1 <= number of cylinders + 1 <= number of heads <= 256 + 1 <= number of sectors/track < 64 +.fi +.Pp +The number of cylinders should be less than or equal to 1024, but this +is not enforced, although a warning will be output. Note that bootable +FreeBSD partitions (the "/" filesystem) must lie completely within the +first 1024 cylinders; if this is not true, booting may fail. +Non-bootable partitions do not have this restriction. +.Pp +Example (all of these are equivalent), for a disk with 1019 cylinders, +39 heads, and 63 sectors: +.Pp +.nf + g c1019 h39 s63 + g h39 c1019 s63 + g s63 h39 c1019 +.fi +.It Xo +.Ic p +.No Ar partition +.No Ar type +.No Ar start +.No Ar length +.Xc +Set the partition given by +.Ar partition +(0-3) to type +.Ar type , +starting at sector +.Ar start +for +.Ar length +sectors. +.Pp +Only those partitions explicitly mentioned by these lines are modified; +any partition not referenced by a "p" line will not be modified. +However, if an invalid partition table is present, or the +.Fl i +option is specified, all existing partition entries will be cleared +(marked as unused), and these "p" lines will have to be used to +explicitly set partition information. If multiple partitions need to be +set, multiple "p" lines must be specified; one for each partition. +.Pp +These partition lines must occur after any geometry specification lines, +if one is present. +.Pp +The +.Ar type +is 165 for FreeBSD partitions. Specifying a partition type of zero is +the same as clearing the partition and marking it as unused; however, +dummy values (such as "0") must still be specified for +.Ar start +and +.Ar length . +.Pp +Note: the start offset will be rounded upwards to a head boundary if +necessary, and the end offset will be rounded downwards to a cylinder +boundary if necessary. +.Pp +Example: to clear partition 3 and mark it as unused: +.Pp +.nf + p 3 0 0 0 +.fi +.Pp +Example: to set partition 0 to a FreeBSD partition, starting at sector 1 +for 2503871 sectors (note: these numbers will be rounded upwards and +downwards to correspond to head and cylinder boundaries): +.Pp +.nf + p 0 165 1 2503871 +.fi +.It Xo +.Ic a +.No Ar partition +.Xc +Make +.Ar partition +the active partition. Can occur anywhere in the config file, but only +one must be present. +.Pp +Example: to make partition 0 the active partition: +.Pp +.nf + a 0 +.fi + +.El +.Pp .Sh SEE ALSO .Xr disklabel 8 .Sh BUGS @@ -203,3 +403,7 @@ Throughout this man page, the term is used where it should actually be .Sq slice , in order to conform with the terms used elsewhere. +.Pp +You cannot use this command to completely dedicate a disk to FreeBSD. The +.Xr disklabel 8 +command must be used for this. diff --git a/sbin/i386/fdisk/fdisk.c b/sbin/i386/fdisk/fdisk.c index 13418c896a8e..b89d86571681 100644 --- a/sbin/i386/fdisk/fdisk.c +++ b/sbin/i386/fdisk/fdisk.c @@ -90,9 +90,31 @@ int dos_cylsecs; static int partition = -1; +#define MAX_ARGS 10 + +static int current_line_number; + +static int geom_processed = 0; +static int part_processed = 0; +static int active_processed = 0; + + +typedef struct cmd { + char cmd; + int n_args; + struct arg { + char argtype; + int arg_val; + } args[MAX_ARGS]; +} CMD; + + static int a_flag = 0; /* set active partition */ static int i_flag = 0; /* replace partition data */ static int u_flag = 0; /* update partition data */ +static int t_flag = 0; /* test only, if f_flag is given */ +static char *f_flag = NULL; /* Read config info from file */ +static int v_flag = 0; /* Be verbose */ static unsigned char bootcode[] = { 0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf, @@ -179,6 +201,7 @@ struct part_type static void print_s0(int which); static void print_part(int i); static void init_sector0(unsigned long start); +static void init_boot(void); static void change_part(int i); static void print_params(); static void change_active(int which); @@ -194,6 +217,8 @@ static int write_s0(); static int ok(char *str); static int decimal(char *str, int *num, int deflt); static char *get_type(int type); +static int read_config(char *config_file); +static void reset_boot(void); #if 0 static int hex(char *str, int *num, int deflt); static int string(char *str, char **ans); @@ -231,11 +256,37 @@ main(int argc, char *argv[]) case 'a': a_flag = 1; break; + case 'f': + if (*token) + { + f_flag = token; + token = ""; + } + else + { + if (argc == 1) + { + goto usage; + } + --argc; + f_flag = *++argv; + } + /* + * u_flag is needed, because we're + * writing to the disk. + */ + u_flag = 1; + break; case 'i': i_flag = 1; case 'u': u_flag = 1; break; + case 't': + t_flag = 1; + case 'v': + v_flag = 1; + break; default: goto usage; } @@ -281,41 +332,75 @@ main(int argc, char *argv[]) } printf("******* Working on device %s *******\n",disk); - if(u_flag) + + if (f_flag) { - get_params_to_use(); + if (read_s0() || i_flag) + { + reset_boot(); + } + + if (!read_config(f_flag)) + { + exit(1); + } + if (v_flag) + { + print_s0(-1); + } + if (!t_flag) + { + write_s0(); + } } else { + if(u_flag) + { + get_params_to_use(); + } + else + { print_params(); - } + } - if (read_s0()) + if (read_s0()) init_sector0(1); - printf("Warning: BIOS sector numbering starts with sector 1\n"); - printf("Information from DOS bootblock is:\n"); - if (partition == -1) + printf("Warning: BIOS sector numbering starts with sector 1\n"); + printf("Information from DOS bootblock is:\n"); + if (partition == -1) for (i = 0; i < NDOSPART; i++) - change_part(i); - else + change_part(i); + else change_part(partition); - if (u_flag || a_flag) + if (u_flag || a_flag) change_active(partition); - if (u_flag || a_flag) { - printf("\nWe haven't changed the partition table yet. "); - printf("This is your last chance.\n"); + if (u_flag || a_flag) { + if (!t_flag) + { + printf("\nWe haven't changed the partition table yet. "); + printf("This is your last chance.\n"); + } print_s0(-1); - if (ok("Should we write new partition table?")) + if (!t_flag) + { + if (ok("Should we write new partition table?")) write_s0(); + } + else + { + printf("\n-t flag specified -- partition table not written.\n"); + } + } } exit(0); usage: - printf("fdisk {-a|-i|-u} [-{0,1,2,3}] [disk]\n"); + printf("fdisk {-a|-i|-u} [-f [-t] [-v]] [-{0,1,2,3}] [disk]\n"); return(1); } @@ -359,14 +444,22 @@ struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i; ,partp->dp_ehd); } + +static void +init_boot(void) +{ + memcpy(mboot.bootinst, bootcode, sizeof(bootcode)); + mboot.signature = BOOT_MAGIC; +} + + static void init_sector0(unsigned long start) { struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]); unsigned long size = disksecs - start; - memcpy(mboot.bootinst, bootcode, sizeof(bootcode)); - mboot.signature = BOOT_MAGIC; + init_boot(); partp->dp_typ = DOSPTYP_386BSD; partp->dp_flag = ACTIVE; @@ -490,6 +583,7 @@ get_params_to_use() } } + /***********************************************\ * Change real numbers into strange dos numbers * \***********************************************/ @@ -779,3 +873,425 @@ get_type(int type) } return("unknown"); } + + +static void +parse_config_line(line, command) + char *line; + CMD *command; +{ + char *cp, *end; + + cp = line; + while (1) /* dirty trick used to insure one exit point for this + function */ + { + memset(command, 0, sizeof(*command)); + + while (isspace(*cp)) ++cp; + if (*cp == '\0' || *cp == '#') + { + break; + } + command->cmd = *cp++; + + /* + * Parse args + */ + while (1) + { + while (isspace(*cp)) ++cp; + if (*cp == '#') + { + break; /* found comment */ + } + if (isalpha(*cp)) + { + command->args[command->n_args].argtype = *cp++; + } + if (!isdigit(*cp)) + { + break; /* assume end of line */ + } + end = NULL; + command->args[command->n_args].arg_val = strtol(cp, &end, 0); + if (cp == end) + { + break; /* couldn't parse number */ + } + cp = end; + command->n_args++; + } + break; + } +} + + +static int +process_geometry(command) + CMD *command; +{ + int status = 1, i; + + while (1) + { + geom_processed = 1; + if (part_processed) + { + fprintf(stderr, + "%s: ERROR line %d: the geometry specification line must occur before\n\ + all partition specifications.\n", + name, current_line_number); + status = 0; + break; + } + if (command->n_args != 3) + { + fprintf(stderr, + "%s: ERROR line %d: incorrect number of geometry args\n", + name, current_line_number); + status = 0; + break; + } + dos_cyls = -1; + dos_heads = -1; + dos_sectors = -1; + for (i = 0; i < 3; ++i) + { + switch (command->args[i].argtype) + { + case 'c': + dos_cyls = command->args[i].arg_val; + break; + case 'h': + dos_heads = command->args[i].arg_val; + break; + case 's': + dos_sectors = command->args[i].arg_val; + break; + default: + fprintf(stderr, + "%s: ERROR line %d: unknown geometry arg type: '%c' (0x%02x)\n", + name, current_line_number, command->args[i].argtype, + command->args[i].argtype); + status = 0; + break; + } + } + if (status == 0) + { + break; + } + + dos_cylsecs = dos_heads * dos_sectors; + + /* + * Do sanity checks on parameter values + */ + if (dos_cyls < 0) + { + fprintf(stderr, + "%s: ERROR line %d: number of cylinders not specified\n", + name, current_line_number); + status = 0; + } + if (dos_cyls == 0 || dos_cyls > 1024) + { + fprintf(stderr, + "%s: WARNING line %d: number of cylinders (%d) may be out-of-range\n\ + (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\ + is dedicated to FreeBSD).\n", + name, current_line_number, dos_cyls); + } + + if (dos_heads < 0) + { + fprintf(stderr, + "%s: ERROR line %d: number of heads not specified\n", + name, current_line_number); + status = 0; + } + else if (dos_heads < 1 || dos_heads > 256) + { + fprintf(stderr, + "%s: ERROR line %d: number of heads must be within (1-256)\n", + name, current_line_number); + status = 0; + } + + if (dos_sectors < 0) + { + fprintf(stderr, "%s: ERROR line %d: number of sectors not specified\n", + name, current_line_number); + status = 0; + } + else if (dos_sectors < 1 || dos_sectors > 63) + { + fprintf(stderr, + "%s: ERROR line %d: number of sectors must be within (1-63)\n", + name, current_line_number); + status = 0; + } + + break; + } + return (status); +} + + +static int +process_partition(command) + CMD *command; +{ + int status = 0, partition; + unsigned long chunks, adj_size, max_end; + struct dos_partition *partp; + + while (1) + { + part_processed = 1; + if (command->n_args != 4) + { + fprintf(stderr, + "%s: ERROR line %d: incorrect number of partition args\n", + name, current_line_number); + break; + } + partition = command->args[0].arg_val; + if (partition < 0 || partition > 3) + { + fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n", + name, current_line_number, partition); + break; + } + partp = ((struct dos_partition *) &mboot.parts) + partition; + bzero((char *)partp, sizeof (struct dos_partition)); + partp->dp_typ = command->args[1].arg_val; + partp->dp_start = command->args[2].arg_val; + partp->dp_size = command->args[3].arg_val; + max_end = partp->dp_start + partp->dp_size; + + if (partp->dp_typ == 0) + { + /* + * Get out, the partition is marked as unused. + */ + /* + * Insure that it's unused. + */ + bzero((char *)partp, sizeof (struct dos_partition)); + status = 1; + break; + } + + /* + * Adjust start upwards, if necessary, to fall on an head boundary. + */ + if (partp->dp_start % dos_sectors != 0) + { + adj_size = + (partp->dp_start / dos_sectors + 1) * dos_sectors; + if (adj_size > max_end) + { + /* + * Can't go past end of partition + */ + fprintf(stderr, + "%s: ERROR line %d: unable to adjust start of partition %d to fall on\n\ + a cylinder boundary.\n", + name, current_line_number, partition); + break; + } + fprintf(stderr, + "%s: WARNING: adjusting start offset of partition '%d' from %d\n\ + to %d, to round to an head boundary.\n", + name, partition, partp->dp_start, adj_size); + partp->dp_start = adj_size; + } + + /* + * Adjust size downwards, if necessary, to fall on a cylinder + * boundary. + */ + chunks = + ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs; + adj_size = chunks - partp->dp_start; + if (adj_size != partp->dp_size) + { + fprintf(stderr, + "%s: WARNING: adjusting size of partition '%d' from %d to %d,\n\ + to round to a cylinder boundary.\n", + name, partition, partp->dp_size, adj_size); + if (chunks > 0) + { + partp->dp_size = adj_size; + } + else + { + partp->dp_size = 0; + } + } + if (partp->dp_size < 1) + { + fprintf(stderr, + "%s: ERROR line %d: size for partition '%d' is zero.\n", + name, current_line_number, partition); + break; + } + + dos(partp->dp_start, partp->dp_size, + &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd); + dos(partp->dp_start+partp->dp_size - 1, partp->dp_size, + &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd); + status = 1; + break; + } + return (status); +} + + +static int +process_active(command) + CMD *command; +{ + int status = 0, partition, i; + struct dos_partition *partp; + + while (1) + { + active_processed = 1; + if (command->n_args != 1) + { + fprintf(stderr, + "%s: ERROR line %d: incorrect number of active args\n", + name, current_line_number); + status = 0; + break; + } + partition = command->args[0].arg_val; + if (partition < 0 || partition > 3) + { + fprintf(stderr, "%s: ERROR line %d: invalid partition number %d\n", + name, current_line_number, partition); + break; + } + /* + * Reset active partition + */ + partp = ((struct dos_partition *) &mboot.parts); + for (i = 0; i < NDOSPART; i++) + partp[i].dp_flag = 0; + partp[partition].dp_flag = ACTIVE; + + status = 1; + break; + } + return (status); +} + + +static int +process_line(line) + char *line; +{ + CMD command; + int status = 1; + + while (1) + { + parse_config_line(line, &command); + switch (command.cmd) + { + case 0: + /* + * Comment or blank line + */ + break; + case 'g': + /* + * Set geometry + */ + status = process_geometry(&command); + break; + case 'p': + status = process_partition(&command); + break; + case 'a': + status = process_active(&command); + break; + default: + status = 0; + break; + } + break; + } + return (status); +} + + +static int +read_config(config_file) + char *config_file; +{ + FILE *fp = NULL; + int status = 1; + char buf[1010]; + + while (1) /* dirty trick used to insure one exit point for this + function */ + { + if (strcmp(config_file, "-") != 0) + { + /* + * We're not reading from stdin + */ + if ((fp = fopen(config_file, "r")) == NULL) + { + status = 0; + break; + } + } + else + { + fp = stdin; + } + current_line_number = 0; + while (!feof(fp)) + { + if (fgets(buf, sizeof(buf), fp) == NULL) + { + break; + } + ++current_line_number; + status = process_line(buf); + if (status == 0) + { + break; + } + } + break; + } + if (fp) + { + /* + * It doesn't matter if we're reading from stdin, as we've reached EOF + */ + fclose(fp); + } + return (status); +} + + +static void +reset_boot(void) +{ + int i; + struct dos_partition *partp; + + init_boot(); + for (i = 0; i < 4; ++i) + { + partp = ((struct dos_partition *) &mboot.parts) + i; + bzero((char *)partp, sizeof (struct dos_partition)); + } +}