From a7d2e82f12bfa5cd15c703fcdcb695d8fad3082e Mon Sep 17 00:00:00 2001
From: Alexander Graf <agraf@suse.de>
Date: Tue, 19 Apr 2011 18:29:04 +0200
Subject: [PATCH] fdasd: add -f flag

This patch adds a force flag to fdasd, allowing it to work on
non-dasd disks as well.

Signed-off-by: Alexander Graf <agraf@suse.de>
Acked-by: John Jolly <jjolly@suse.de>
---
 fdasd/fdasd.c |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 fdasd/fdasd.h |    3 +-
 2 files changed, 72 insertions(+), 12 deletions(-)

--- a/fdasd/fdasd.c
+++ b/fdasd/fdasd.c
@@ -16,6 +16,8 @@
 struct hd_geometry geo;
 char line_buffer[LINE_LENGTH];
 char *line_ptr = line_buffer;
+int force;
+int fake_dasd;
 
 /* Full tool name */
 static const char tool_name[] = "fdasd: zSeries DASD partitioning program";
@@ -98,6 +100,29 @@ static void get_addr_of_highest_f1_f8_la
 	vtoc_set_cchhb(addr, VTOC_START_CC, VTOC_START_HH, record);
 }
 
+static inline off_t get_cyl(int fd)
+{
+	struct stat stat;
+	struct hd_geometry geo;
+	int r;
+	off_t size;
+
+	r = ioctl(fd, HDIO_GETGEO, &geo);
+	if (!r) {
+		/* real disk */
+		size = geo.heads * geo.sectors * geo.cylinders;
+	} else {
+		/* image file */
+		r = fstat(fd, &stat);
+		if (!r)
+			size = stat.st_size / 4096;
+		else
+			size = 0x1000;
+	}
+
+	return size / 15 / 12;
+}
+
 /* 
  * Check for valid volume serial characters (max. 6) - remove invalid.
  * If volser is empty, fill with default volser. 
@@ -348,7 +373,8 @@ fdasd_usage (void)
 		                          "using information\n"
                 "                         found in CONFIGFILE\n"
 		"-i, --volser             Print volume serial\n"
-		"-p, --table              Print partition table\n");
+		"-p, --table              Print partition table\n"
+		"-f, --force              Force creation on non-DASD device\n");
 }
 
 
@@ -514,6 +540,9 @@ fdasd_parse_options (fdasd_anchor_t *anc
 		case 'k':
 			anc->keep_volser++;
 			break;
+		case 'f':
+			force = 1;
+			break;
 		case -1:
 			/* End of options string - start of devices list */
 			break;
@@ -719,7 +748,7 @@ fdasd_verify_device (fdasd_anchor_t *anc
 		fdasd_error(anc, device_verification_failed, err_str);
 	}
 
-	if (!(S_ISBLK (dst.st_mode))) {
+	if (!(S_ISBLK (dst.st_mode)) && !force) {
 		snprintf(err_str, ERROR_STRING_SIZE,
 			 "Device '%s' (%d/%d) is not a block device\n", name,
 			 (unsigned short) major(dst.st_rdev),
@@ -987,6 +1016,7 @@ fdasd_get_volser(fdasd_anchor_t *anc, ch
 	int fd;
 	volume_label_t vlabel;
 	char err_str[ERROR_STRING_SIZE];
+	int r;
 
 	if ((fd = open(devname, O_RDONLY)) == -1) {
 		snprintf(err_str, ERROR_STRING_SIZE,
@@ -995,13 +1025,23 @@ fdasd_get_volser(fdasd_anchor_t *anc, ch
 		fdasd_error(anc, unable_to_open_disk, err_str);
 	}     
 
-	if (ioctl(fd, BIODASDINFO, &dasd_info) != 0) {
+	r = ioctl(fd, BIODASDINFO, &dasd_info);
+	if ((r != 0) && force) {
+		fake_dasd = 1;
+		memset(&dasd_info, 0, sizeof(dasd_info));
+		memcpy(dasd_info.type, "ECKD", 4);
+                dasd_info.cu_type = 0;
+                dasd_info.label_block = 2;
+	} else if ((r != 0)) {
 		close(fd);
 		fdasd_error(anc, unable_to_ioctl, 
 			    "Could not retrieve disk information.");
 	}
 
-	if (ioctl(fd, BLKSSZGET, &blksize) != 0) {
+	r = ioctl(fd, BLKSSZGET, &blksize);
+	if ((r != 0) && fake_dasd) {
+		blksize = 4096;
+	} else if (r != 0) {
 		close(fd);
 	        fdasd_error(anc, unable_to_ioctl,
 			    "Could not retrieve blocksize information.");
@@ -1906,7 +1946,7 @@ fdasd_check_disk_access (fdasd_anchor_t
 {
 	char err_str[ERROR_STRING_SIZE];
 	format1_label_t f1;
-	int fd, pos, ro;
+	int fd, pos, ro = 0;
 
         if ((fd = open(options.device, O_RDWR)) == -1) {
 		snprintf(err_str, ERROR_STRING_SIZE,
@@ -1939,7 +1979,7 @@ fdasd_check_disk_access (fdasd_anchor_t
 		fdasd_error(anc, unable_to_seek_disk, err_str);
         }
 	
-	if (ioctl(fd, BLKROGET, &ro) != 0) {
+	if (!fake_dasd && ioctl(fd, BLKROGET, &ro) != 0) {
 		snprintf(err_str, ERROR_STRING_SIZE, 
 			 "Could not get read-only status for device '%s'.",
 			 options.device);
@@ -1960,7 +2000,7 @@ fdasd_check_disk_access (fdasd_anchor_t
 static void
 fdasd_get_geometry (fdasd_anchor_t *anc) 
 {
-        int fd, blksize = 0;
+        int fd, blksize = 0, r;
 	dasd_information_t dasd_info;
 	char err_str[ERROR_STRING_SIZE];
 	struct dasd_eckd_characteristics *characteristics;
@@ -1972,21 +2012,36 @@ fdasd_get_geometry (fdasd_anchor_t *anc)
 		fdasd_error(anc, unable_to_open_disk, err_str);
 	}
 
-	if (ioctl(fd, HDIO_GETGEO, &geo) != 0) {
+	r = ioctl(fd, HDIO_GETGEO, &geo);
+	if (force) {
+		fake_dasd = 1;
+                geo.heads = 15;
+                geo.sectors = 12;
+		geo.cylinders = get_cyl(fd);
+	} else if (r != 0) {
 	        close(fd);
 		fdasd_error(anc, unable_to_ioctl, 
 			    "Could not retrieve disk geometry information.");
 	}
 
-	if (ioctl(fd, BLKSSZGET, &blksize) != 0) {
+	if (fake_dasd) {
+		blksize = 4096;
+	} else if (ioctl(fd, BLKSSZGET, &blksize) != 0) {
 	        close(fd);
 	        fdasd_error(anc, unable_to_ioctl,
 			    "Could not retrieve blocksize information.");
 	}
 
 	/* get disk type */
-	if (ioctl(fd, BIODASDINFO, &dasd_info) != 0) {
-	        close(fd);
+	r = ioctl(fd, BIODASDINFO, &dasd_info);
+	if ((r != 0) && force) {
+		fake_dasd = 1;
+		memset(&dasd_info, 0, sizeof(dasd_info));
+		memcpy(dasd_info.type, "ECKD", 4);
+                dasd_info.cu_type = 0;
+                dasd_info.label_block = 2;
+	} else if ((r != 0)) {
+		close(fd);
 		fdasd_error(anc, unable_to_ioctl, 
 			    "Could not retrieve disk information.");
 	}
@@ -1999,6 +2054,10 @@ fdasd_get_geometry (fdasd_anchor_t *anc)
 	else
 		anc->hw_cylinders = characteristics->no_cyl;
 
+	if (fake_dasd) {
+		anc->hw_cylinders = geo.cylinders;
+	}
+
 	close(fd);
 
 	if (strncmp(dasd_info.type, "ECKD", 4) != 0) {
--- a/fdasd/fdasd.h
+++ b/fdasd/fdasd.h
@@ -159,11 +159,12 @@ static struct option fdasd_long_options[
 	{ "table",       no_argument,       NULL, 'p'},
 	{ "volser",      no_argument,       NULL, 'i'},
 	{ "keep_volser", no_argument,       NULL, 'k'},
+	{ "force",       no_argument,       NULL, 'f'},
 	{ 0,             0,                 0,    0  }
 };
 
 /* Command line option abbreviations */
-static const char option_string[] = "vasrl:c:hpik";
+static const char option_string[] = "vasrl:c:hpikf";
 
 struct fdasd_options {
 	char *device;
