فهرست منبع

rework installation scripts for block devices

Avoid sfdisk, parted and chroot to be more portable.
Compile Host grub in target/tools, only used for ibmx40 target
right now. Add pt - partition table manager from OpenWrt with some
additions and portability fixes.
Cfgfs support missing, coming soon.
Waldemar Brodkorb 13 سال پیش
والد
کامیت
8f5add1754
6فایلهای تغییر یافته به همراه488 افزوده شده و 158 حذف شده
  1. 152 0
      scripts/install-rb532.sh
  2. 44 156
      scripts/install.sh
  3. 7 1
      target/ibmx40/Makefile
  4. 24 0
      target/tools/grub/Makefile
  5. 5 1
      tools/adk/Makefile
  6. 256 0
      tools/adk/pt.c

+ 152 - 0
scripts/install-rb532.sh

@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+# This file is part of the OpenADK project. OpenADK is copyrighted
+# material, please see the LICENCE file in the top-level directory.
+
+if [ $(id -u) -ne 0 ];then
+	printf "Installation is only possible as root\n"
+	exit 1
+fi
+
+printf "Checking if sfdisk is installed"
+sfdisk=$(which sfdisk)
+
+if [ ! -z $sfdisk -a -x $sfdisk ];then
+	printf "...okay\n"
+else
+	printf "...failed\n"
+	exit 1
+fi
+
+printf "Checking if parted is installed"
+parted=$(which parted)
+
+if [ ! -z $parted -a -x $parted ];then
+	printf "...okay\n"
+else
+	printf "...failed\n"
+	exit 1
+fi
+
+printf "Checking if mke2fs is installed"
+mke2fs=$(which mke2fs)
+
+if [ ! -z $mke2fs -a -x $mke2fs ];then
+	printf "...okay\n"
+else
+	printf "...failed\n"
+	exit 1
+fi
+
+printf "Checking if tune2fs is installed"
+tune2fs=$(which tune2fs)
+
+if [ ! -z $tune2fs -a -x $tune2fs ];then
+	printf "...okay\n"
+else
+	printf "...failed\n"
+	exit 1
+fi
+
+if [ -z $1 ];then
+	printf "Please give your compact flash or USB device as first parameter\n"
+	exit 1
+else
+	if [ -z $2 ];then
+		printf "Please give your install tar archive as second parameter\n"
+		exit 2
+	fi
+	if [ -f $2 ];then
+		printf "Installing $2 on $1\n"
+	else
+		printf "$2 is not a file, Exiting\n"
+		exit 1
+	fi
+	if [ -z $3 ];then
+		printf "Please give the kernel as third parameter\n"
+		exit 2
+	fi
+	if [ -f $3 ];then
+		printf "Installing $3 on $1\n"
+	else
+		printf "$3 is not a file, Exiting\n"
+		exit 1
+	fi
+	if [ -b $1 ];then
+		printf "Using $1 as CF/USB disk for installation\n"
+		printf "This will destroy all data on $1, are you sure?\n"
+		printf "Type "y" to continue\n"
+		read y
+		if [ "$y" = "y" ];then
+			$sfdisk -l $1 2>&1 |grep 'No medium'
+			if [ $? -eq 0 ];then
+				exit 1
+			else
+				printf "Starting with installation\n"
+			fi
+		else
+			printf "Exiting.\n"
+			exit 1
+		fi
+	else
+		printf "Sorry $1 is not a block device\n"
+		exit 1
+	fi
+fi
+	
+
+if [ $(mount | grep $1| wc -l) -ne 0 ];then
+	printf "Block device $1 is in use, please umount first.\n"
+	exit 1
+fi
+
+
+if [ $($sfdisk -l $1 2>/dev/null|grep Empty|wc -l) -ne 4 ];then
+	printf "Partitions already exist, should I wipe them?\n"
+	printf "Type y to continue\n"
+	read y
+	if [ $y = "y" ];then
+		printf "Wiping existing partitions\n"
+		dd if=/dev/zero of=$1 bs=512 count=1 >/dev/null 2>&1
+	else
+		printf "Exiting.\n"
+		exit 1
+	fi
+fi
+
+printf "Create partition and filesystem for rb532\n"
+rootpart=${1}2
+$parted -s $1 mklabel msdos
+sleep 2
+maxsize=$(env LC_ALL=C $parted $1 -s unit cyl print |awk '/^Disk/ { print $3 }'|sed -e 's/cyl//')
+rootsize=$(($maxsize-2))
+
+$parted -s $1 unit cyl mkpart primary ext2 0 1
+$parted -s $1 unit cyl mkpart primary ext2 1 $rootsize
+$parted -s $1 unit cyl mkpart primary fat32 $rootsize $maxsize
+$parted -s $1 set 1 boot on
+$sfdisk --change-id $1 1 27
+$sfdisk --change-id $1 3 88
+sleep 2
+$mke2fs ${1}2
+sync
+dd if=$3 of=${1}1 bs=2048
+sync
+sleep 2
+$tune2fs -c 0 -i 0 -m 1 ${rootpart} >/dev/null
+if [ $? -eq 0 ];then
+	printf "Successfully disabled filesystem checks on ${rootpart}\n"
+else	
+	printf "Disabling filesystem checks failed, Exiting.\n"
+	exit 1
+fi	
+
+tmp=$(mktemp -d)
+mount -t ext2 ${rootpart} $tmp
+printf "Extracting install archive\n"
+tar -C $tmp -xzpf $2 
+printf "Fixing permissions\n"
+chmod 1777 $tmp/tmp
+chmod 4755 $tmp/bin/busybox
+umount $tmp
+printf "Successfully installed.\n"
+exit 0

+ 44 - 156
scripts/install.sh

@@ -1,26 +1,11 @@
 #!/usr/bin/env bash
-if [ $(id -u) -ne 0 ];then
-	printf "Installation is only possible as root\n"
-	exit 1
-fi
-
-printf "Checking if sfdisk is installed"
-sfdisk=$(which sfdisk)
-
-if [ ! -z $sfdisk -a -x $sfdisk ];then
-	printf "...okay\n"
-else
-	printf "...failed\n"
-	exit 1
-fi
+# This file is part of the OpenADK project. OpenADK is copyrighted
+# material, please see the LICENCE file in the top-level directory.
 
-printf "Checking if parted is installed"
-parted=$(which parted)
+TOPDIR=$(pwd)
 
-if [ ! -z $parted -a -x $parted ];then
-	printf "...okay\n"
-else
-	printf "...failed\n"
+if [ $(id -u) -ne 0 ];then
+	printf "Installation is only possible as root\n"
 	exit 1
 fi
 
@@ -34,27 +19,17 @@ else
 	exit 1
 fi
 
-printf "Checking if tune2fs is installed"
-tune2fs=$(which tune2fs)
-
-if [ ! -z $tune2fs -a -x $tune2fs ];then
-	printf "...okay\n"
-else
-	printf "...failed\n"
-	exit 1
-fi
-
 cfgfs=1
-rb532=0
-while getopts "nr" option
+quiet=0
+while getopts "nq" option
 do
 	case $option in
+		q)
+			quiet=1
+			;;
 		n)
 			cfgfs=0
 			;;
-		r)
-			rb532=1
-			;;
 		*)
 			printf "Option not recognized\n"
 			exit 1
@@ -78,33 +53,12 @@ else
 		printf "$2 is not a file, Exiting\n"
 		exit 1
 	fi
-	if [ $rb532 -eq 1 ];then
-		if [ -z $3 ];then
-			printf "Please give the kernel as third parameter\n"
-			exit 2
-		fi
-		if [ -f $3 ];then
-			printf "Installing $3 on $1\n"
-		else
-			printf "$3 is not a file, Exiting\n"
-			exit 1
-		fi
-	fi
 	if [ -b $1 ];then
 		printf "Using $1 as CF/USB disk for installation\n"
-		printf "This will destroy all data on $1, are you sure?\n"
-		printf "Type "y" to continue\n"
-		read y
-		if [ "$y" = "y" ];then
-			$sfdisk -l $1 2>&1 |grep 'No medium'
-			if [ $? -eq 0 ];then
-				exit 1
-			else
-				printf "Starting with installation\n"
-			fi
-		else
-			printf "Exiting.\n"
-			exit 1
+		if [ $quiet -eq 0 ];then
+			printf "This will destroy all data on $1, are you sure?\n"
+			printf "Type "y" to continue\n"
+			read y
 		fi
 	else
 		printf "Sorry $1 is not a block device\n"
@@ -112,26 +66,11 @@ else
 	fi
 fi
 	
-
 if [ $(mount | grep $1| wc -l) -ne 0 ];then
 	printf "Block device $1 is in use, please umount first.\n"
 	exit 1
 fi
 
-
-if [ $($sfdisk -l $1 2>/dev/null|grep Empty|wc -l) -ne 4 ];then
-	printf "Partitions already exist, should I wipe them?\n"
-	printf "Type y to continue\n"
-	read y
-	if [ $y = "y" ];then
-		printf "Wiping existing partitions\n"
-		dd if=/dev/zero of=$1 bs=512 count=1 >/dev/null 2>&1
-	else
-		printf "Exiting.\n"
-		exit 1
-	fi
-fi
-
 case $2 in
 	wrap*)
 		speed=38400
@@ -141,102 +80,51 @@ case $2 in
 		;;
 esac
 
-if [ $rb532 -ne 0 ];then
-	printf "Create partition and filesystem for rb532\n"
-	rootpart=${1}2
-	$parted -s $1 mklabel msdos
-	sleep 2
-	maxsize=$(env LC_ALL=C $parted $1 -s unit cyl print |awk '/^Disk/ { print $3 }'|sed -e 's/cyl//')
-	rootsize=$(($maxsize-2))
-
-	$parted -s $1 unit cyl mkpart primary ext2 0 1
-	$parted -s $1 unit cyl mkpart primary ext2 1 $rootsize
-	$parted -s $1 unit cyl mkpart primary fat32 $rootsize $maxsize
-	$parted -s $1 set 1 boot on
-	$sfdisk --change-id $1 1 27
-	$sfdisk --change-id $1 3 88
-	sleep 2
-	$mke2fs ${1}2
-	sync
-	dd if=$3 of=${1}1 bs=2048
-	sync
-else
-	rootpart=${1}1
-	if [ $cfgfs -eq 0 ];then
-		printf "Create partition and filesystem without cfgfs\n"
-$sfdisk $1 << EOF
-,,L
-;
-;
-;
-y
-EOF
-		$mke2fs ${rootpart}
-	else
-		printf "Create partition and filesystem with cfgfs\n"
-		$parted -s $1 mklabel msdos
-		sleep 2
-		maxsize=$(env LC_ALL=C $parted $1 -s unit cyl print |awk '/^Disk/ { print $3 }'|sed -e 's/cyl//')
-		rootsize=$(($maxsize-2))
-
-		$parted -s $1 unit cyl mkpart primary ext2 0 $rootsize
-		$parted -s $1 unit cyl mkpart primary fat32 $rootsize $maxsize
-		$parted -s $1 set 1 boot on
-		$sfdisk --change-id $1 2 88
-		$mke2fs ${1}1
-	fi
-fi
-
-if [ $? -eq 0 ];then
-	printf "Successfully created partition ${rootpart}\n"
-else
-	printf "Partition creation failed, Exiting.\n"
-	exit 1
-fi
-
-sleep 2
-$tune2fs -c 0 -i 0 -m 1 ${rootpart} >/dev/null
-if [ $? -eq 0 ];then
-	printf "Successfully disabled filesystem checks on ${rootpart}\n"
-else	
-	printf "Disabling filesystem checks failed, Exiting.\n"
-	exit 1
-fi	
-
+rootpart=${1}1
+pt=$TOPDIR/bin/tools/pt
+# get sector size from block device
+maxsector=$(sudo $pt -g $1)
+head=16
+sect=63
+rsize=$(($maxsector / 2))
+
+printf "Creating partition table ...\n"
+table=$(mktemp)
+# generate partition table
+$pt -o $table -s $sect -h $head -p ${rsize}K
+# write partition table to block device
+dd if=$table of=$1 bs=512 count=1 2> /dev/null
+printf "Creating ext2 filesystem ...\n"
+$mke2fs -q ${1}1
+
+printf "Extracting install archive ...\n"
 tmp=$(mktemp -d)
 mount -t ext2 ${rootpart} $tmp
-printf "Extracting install archive\n"
 tar -C $tmp -xzpf $2 
-printf "Fixing permissions\n"
+printf "Fixing permissions ...\n"
 chmod 1777 $tmp/tmp
 chmod 4755 $tmp/bin/busybox
 
-if [ $rb532 -eq 0 ];then
-	printf "Installing GRUB bootloader\n"
-	mkdir -p $tmp/boot/grub
-	mount -o bind /dev $tmp/dev
-	chroot $tmp mount -t proc /proc /proc
-	chroot $tmp mount -t sysfs /sys /sys
+printf "Installing GRUB bootloader ...\n"
+mkdir -p $tmp/boot/grub
 cat << EOF > $tmp/boot/grub/grub.cfg
 set default=0
 set timeout=1
-serial --unit=0 --speed=$speed
-terminal_output serial 
-terminal_input serial 
+terminal_output console
+terminal_input console
 
 menuentry "GNU/Linux (OpenADK)" {
 	insmod ext2
 	set root=(hd0,1)
-	linux /boot/vmlinuz-adk root=/dev/sda1 ro init=/init console=ttyS0,$speed console=tty0 panic=10
+	linux /boot/vmlinuz-adk init=/init
 }
 EOF
-	chroot $tmp grub-install $1
-	umount $tmp/proc
-	umount $tmp/sys
-	umount $tmp/dev
-fi
-
+./bin/tools/sbin/grub-install \
+	--grub-setup=./bin/tools/sbin/grub-setup \
+	--grub-mkimage=./bin/tools/bin/grub-mkimage \
+	--grub-mkdevicemap=./bin/tools/sbin/grub-mkdevicemap \
+	--no-floppy --root-directory=$tmp $1
 umount $tmp
-
 printf "Successfully installed.\n"
+rm -rf $tmp
 exit 0

+ 7 - 1
target/ibmx40/Makefile

@@ -7,6 +7,12 @@ include $(TOPDIR)/mk/modules.mk
 include $(TOPDIR)/mk/kernel-build.mk
 include $(TOPDIR)/mk/image.mk
 
+$(TOOLS_BUILD_DIR):
+	mkdir -p $(TOOLS_BUILD_DIR)
+
+tools-compile: $(TOOLS_BUILD_DIR)
+	$(MAKE) -C ../tools/grub prepare compile install
+
 KERNEL:=$(LINUX_DIR)/arch/x86/boot/bzImage
 
 createinitcrypt:
@@ -26,7 +32,7 @@ imageinstall: $(BIN_DIR)/$(ROOTFSTARBALL)
 	@echo "The RootFS tarball is: $(BIN_DIR)/$(ROOTFSTARBALL)"
 endif
 ifeq ($(FS),usb)
-imageinstall: $(BIN_DIR)/$(ROOTFSTARBALL)
+imageinstall: tools-compile $(BIN_DIR)/$(ROOTFSTARBALL)
 	@echo "The RootFS tarball is: $(BIN_DIR)/$(ROOTFSTARBALL)"
 	@echo "To install everything to USB use scripts/install.sh"
 endif

+ 24 - 0
target/tools/grub/Makefile

@@ -0,0 +1,24 @@
+# This file is part of the OpenADK project. OpenADK is copyrighted
+# material, please see the LICENCE file in the top-level directory.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=		grub
+PKG_VERSION:=		1.98
+PKG_RELEASE:=		1
+PKG_MD5SUM:=		c0bcf60e524739bb64e3a2d4e3732a59
+PKG_SITES:=		ftp://alpha.gnu.org/gnu/grub/
+
+include ../rules.mk
+
+$(WRKBUILD)/.compiled: ${WRKDIST}/.prepared
+	cd $(WRKBUILD) && ./configure --prefix=$(TOPDIR)/bin/tools
+	$(MAKE) -C $(WRKBUILD)
+	touch $@
+
+$(TOPDIR)/bin/tools/bin/grub-install: $(WRKBUILD)/.compiled
+	$(MAKE) -C $(WRKBUILD) install
+
+install: $(TOPDIR)/bin/tools/bin/grub-install
+
+include $(TOPDIR)/mk/tools.mk

+ 5 - 1
tools/adk/Makefile

@@ -9,6 +9,10 @@ ${TOPDIR}/bin/tools/depmaker:
 ${TOPDIR}/bin/tools/pkgrebuild:
 	$(HOSTCC) -o $(TOPDIR)/bin/tools/pkgrebuild pkgrebuild.c strmap.c
 
-install: ${TOPDIR}/bin/tools/depmaker ${TOPDIR}/bin/tools/pkgrebuild
+${TOPDIR}/bin/tools/pt:
+	$(HOSTCC) -o $(TOPDIR)/bin/tools/pt pt.c
+
+install: ${TOPDIR}/bin/tools/depmaker ${TOPDIR}/bin/tools/pkgrebuild \
+	$(TOPDIR)/bin/tools/pt
 
 include $(TOPDIR)/mk/tools.mk

+ 256 - 0
tools/adk/pt.c

@@ -0,0 +1,256 @@
+/* 
+ * pt - partition table utility
+ * Copyright (C) 2010 by Waldemar Brodkorb <wbx@openadk.org>
+ * 
+ * just adds some required code to ptgen - partition table generator
+ * Copyright (C) 2006 by Felix Fietkau <nbd@openwrt.org>
+ *
+ * uses parts of afdisk
+ * Copyright (C) 2002 by David Roetzel <david@roetzel.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#endif
+
+#if defined(__APPLE__)
+#include <sys/disk.h>
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT
+#endif
+
+#define bswap16(x) ( \
+	 ((((x)    )&(unsigned int)0xff)<< 8) \
+	|((((x)>> 8)&(unsigned int)0xff)    ) \
+)
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(x) bswap16(x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#else
+#error unknown endianness!
+#endif
+
+/* Partition table entry */
+struct pte { 
+	unsigned char active;
+	unsigned char chs_start[3];
+	unsigned char type;
+	unsigned char chs_end[3];
+	unsigned int start;
+	unsigned int length;
+};
+
+struct partinfo {
+	unsigned long size;
+	int type;
+};
+
+int verbose = 0;
+int active = 1;
+int heads = -1;
+int sectors = -1;
+struct partinfo parts[4];
+char *filename = NULL;
+
+/*
+ * get the sector size of the block device
+ *
+ * print the sector size
+ */
+
+static void getmaxsize(char *device) {
+	int fd;
+	unsigned long maxsectors=0;
+
+	fd = open(device, O_RDONLY);
+	ioctl(fd, BLKGETSIZE, &maxsectors);
+	printf("%lu\n", maxsectors);
+	close(fd);
+}
+
+/* 
+ * parse the size argument, which is either
+ * a simple number (K assumed) or
+ * K, M or G
+ *
+ * returns the size in KByte
+ */
+static long to_kbytes(const char *string) {
+	int exp = 0;
+	long result;
+	char *end;
+
+	result = strtoul(string, &end, 0);
+	switch (tolower(*end)) {
+			case 'k' :
+			case '\0' : exp = 0; break;
+			case 'm' : exp = 1; break;
+			case 'g' : exp = 2; break;
+			default: return 0;
+	}
+
+	if (*end)
+		end++;
+
+	if (*end) {
+		fprintf(stderr, "garbage after end of number\n");
+		return 0;
+	}
+
+	/* result: number + 1024^(exp) */
+	return result * ((2 << ((10 * exp) - 1)) ?: 1);
+}
+
+/* convert the sector number into a CHS value for the partition table */
+static void to_chs(long sect, unsigned char chs[3]) {
+	int c,h,s;
+	
+	s = (sect % sectors) + 1;
+	sect = sect / sectors;
+	h = sect % heads;
+	sect = sect / heads;
+	c = sect;
+
+	chs[0] = h;
+	chs[1] = s | ((c >> 2) & 0xC0);
+	chs[2] = c & 0xFF;
+
+	return;
+}
+
+/* round the sector number up to the next cylinder */
+static inline unsigned long round_to_cyl(long sect) {
+	int cyl_size = heads * sectors;
+
+	return sect + cyl_size - (sect % cyl_size); 
+}
+
+/* check the partition sizes and write the partition table */
+static int gen_ptable(int nr)
+{
+	struct pte pte[4];
+	unsigned long sect = 0; 
+	unsigned int start, len;
+	int i, fd, ret = -1;
+
+	memset(pte, 0, sizeof(struct pte) * 4);
+	for (i = 0; i < nr; i++) {
+		if (!parts[i].size) {
+			fprintf(stderr, "Invalid size in partition %d!\n", i);
+			return -1;
+		}
+		pte[i].active = ((i + 1) == active) ? 0x80 : 0;
+		pte[i].type = parts[i].type;
+		pte[i].start = cpu_to_le16(start = sect + sectors);
+		sect = round_to_cyl(start + parts[i].size * 2);
+		pte[i].length = cpu_to_le16(len = sect - start);
+		to_chs(start, pte[i].chs_start);
+		to_chs(start + len - 1, pte[i].chs_end);
+		if (verbose)
+			fprintf(stderr, "Partition %d: start=%u, end=%u, size=%u\n", i, start * 512, (start + len) * 512, len * 512);
+	}
+
+	if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
+		fprintf(stderr, "Can't open output file '%s'\n",filename);
+		return -1;
+	}
+
+	lseek(fd, 446, SEEK_SET);
+	if (write(fd, pte, sizeof(struct pte) * 4) != sizeof(struct pte) * 4) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+	lseek(fd, 510, SEEK_SET);
+	if (write(fd, "\x55\xaa", 2) != 2) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+	
+	ret = 0;
+fail:
+	close(fd);
+	return ret;
+}
+
+static void usage(char *prog)
+{
+	fprintf(stderr,	"Usage: %s [-v] -h <heads> -s <sectors> -o <outputfile> [-a 0..4] [[-t <type>] -p <size>...] \n", prog);
+	fprintf(stderr,	"Usage: %s -g <device>\n", prog);
+	exit(1);
+}
+
+int main (int argc, char **argv)
+{
+	char type = 0x83;
+	int ch;
+	int part = 0;
+
+	while ((ch = getopt(argc, argv, "h:s:p:a:t:o:vg:")) != -1) {
+		switch (ch) {
+		case 'o':
+			filename = optarg;
+			break;
+		case 'v':
+			verbose++;
+			break;
+		case 'h':
+			heads = (int) strtoul(optarg, NULL, 0);
+			break;
+		case 's':
+			sectors = (int) strtoul(optarg, NULL, 0);
+			break;
+		case 'p':
+			if (part > 3) {
+				fprintf(stderr, "Too many partitions\n");
+				exit(1);
+			}
+			parts[part].size = to_kbytes(optarg);
+			parts[part++].type = type;
+			break;
+		case 't':
+			type = (char) strtoul(optarg, NULL, 16);
+			break;
+		case 'a':
+			active = (int) strtoul(optarg, NULL, 0);
+			if ((active < 0) || (active > 4))
+				active = 0;
+			break;
+		case 'g':
+			getmaxsize(optarg);
+			exit(0);
+		case '?':
+		default:
+			usage(argv[0]);
+		}
+	}
+	argc -= optind;
+	if (argc || (heads <= 0) || (sectors <= 0) || !filename) 
+		usage(argv[0]);
+	
+	return gen_ptable(part);
+}