diff options
Diffstat (limited to 'libparted/labels/fdasd.c')
-rw-r--r-- | libparted/labels/fdasd.c | 1153 |
1 files changed, 1153 insertions, 0 deletions
diff --git a/libparted/labels/fdasd.c b/libparted/labels/fdasd.c new file mode 100644 index 0000000..df32205 --- /dev/null +++ b/libparted/labels/fdasd.c @@ -0,0 +1,1153 @@ +/* + * File...........: arch/s390/tools/fdasd.c + * Author(s)......: Volker Sameske <sameske@de.ibm.com> + * Bugreports.to..: <Linux390@de.ibm.com> + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2001 + * + * History of changes (starts March 2001) + * 2001-04-11 possibility to change volume serial added + * possibility to change partition type added + * some changes to DS4HPCHR and DS4DSREC + * 2001-05-03 check for invalid partition numbers added + * wrong free_space calculation bug fixed + * 2001-06-26 '-a' option added, it is now possible to add a single + * partition in non-interactive mode + * 2001-06-26 long parameter support added + * + */ + +#include <parted/vtoc.h> +#include <parted/fdasd.h> + +#include <parted/parted.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define GETARG(x) {int k=strlen(optarg);x=malloc(k);strncpy(x,optarg,k);} + +static int +getpos (fdasd_anchor_t *anc, int dsn) +{ + PDEBUG + return anc->partno[dsn]; +} + +static int +getdsn (fdasd_anchor_t *anc, int pos) +{ + PDEBUG + int i; + + for (i=0; i<USABLE_PARTITIONS; i++) { + if (anc->partno[i] == pos) + return i; + } + + return -1; +} + +static void +setpos (fdasd_anchor_t *anc, int dsn, int pos) +{ + PDEBUG + anc->partno[dsn] = pos; +} + +static void +fdasd_check_volser (char *s, int devno) +{ + PDEBUG + int i, j; + + for (i = 0; i < 6; i++) { + if ((s[i] < 0x20) || (s[i] > 0x7a) + || ((s[i] >= 0x21) && (s[i] <= 0x22)) + || /* !" */ ((s[i] >= 0x26) && (s[i] <= 0x2f)) + || /* &'()*+,-./ */ ((s[i] >= 0x3a) && (s[i] <= 0x3f)) + || /* :;<=>? */ ((s[i] >= 0x5b) && (s[i] <= 0x60))) + /* \]^_ยด */ s[i] = ' '; + s[i] = toupper (s[i]); + } + + s[6] = 0x00; + + for (i = 0; i < 6; i++) { + if (s[i] == ' ') + for (j = i; j < 6; j++) + if (s[j] != ' ') { + s[i] = s[j]; + s[j] = ' '; + break; + } + } + + if (s[0] == ' ') { + printf ("Usage error, switching to default.\n"); + sprintf (s, "0X%04x", devno); + for (i = 0; i < 6; i++) + s[i] = toupper (s[i]); + } +} + +void +fdasd_cleanup (fdasd_anchor_t *anchor) +{ + PDEBUG + int i; + partition_info_t *p, *q; + + if (anchor == NULL) + return; + + if (anchor->f4 != NULL) + free(anchor->f4); + + if (anchor->f5 != NULL) + free(anchor->f5); + + if (anchor->f7 != NULL) + free(anchor->f7); + + if (anchor->vlabel != NULL) + free(anchor->vlabel); + + p = anchor->first; + if (p == NULL) + return; + + for (i=1; i <= USABLE_PARTITIONS; i++) { + if (p == NULL) + return; + q = p->next; + free(p); + p = q; + } +} + +static void +fdasd_error (fdasd_anchor_t *anc, enum fdasd_failure why, char * str) +{ + PDEBUG + char error[2*LINE_LENGTH], *message = error; + + switch (why) { + case unable_to_open_disk: + sprintf(error, _("%s open error\n%s\n"), + FDASD_ERROR, str); + break; + case unable_to_seek_disk: + sprintf(error, _("%s seek error\n%s\n"), FDASD_ERROR, str); + break; + case unable_to_read_disk: + sprintf(error, _("%s read error\n%s\n"), FDASD_ERROR, str); + break; + case read_only_disk: + sprintf(error, _("%s write error\n%s\n"), FDASD_ERROR, str); + break; + case unable_to_ioctl: + sprintf(error, _("%s IOCTL error\n%s\n"), FDASD_ERROR, str); + break; + case api_version_mismatch: + sprintf(error, _("%s API version mismatch\n%s\n"), FDASD_ERROR,str); + break; + case wrong_disk_type: + sprintf(error, _("%s Unsupported disk type\n%s\n"), + FDASD_ERROR, str); + break; + case wrong_disk_format: + sprintf(error, _("%s Unsupported disk format\n%s\n"), + FDASD_ERROR, str); + break; + case disk_in_use: + sprintf(error, _("%s Disk in use\n%s\n"), FDASD_ERROR, str); + break; + case config_syntax_error: + sprintf(error, _("%s Config file syntax error\n%s\n"), + FDASD_ERROR, str); + break; + case vlabel_corrupted: + sprintf(error, _("%s Volume label is corrupted.\n%s\n"), + FDASD_ERROR, str); + break; + case dsname_corrupted: + sprintf(error, _("%s a data set name is corrupted.\n%s\n"), + FDASD_ERROR, str); + break; + case malloc_failed: + sprintf(error, _("%s space allocation\n%s\n"), + FDASD_ERROR, str); + break; + case device_verification_failed: + sprintf(error, _("%s device verification failed\n" \ + "The specified device is not a valid DASD device\n"), + FDASD_ERROR); + break; + default: + sprintf(error, _("%s Fatal error\n%s\n"), FDASD_ERROR, str); + } + + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, message); +} + +/* + * converts cyl-cyl-head-head-blk to blk + */ +static unsigned long +cchhb2blk (cchhb_t *p, struct fdasd_hd_geometry *geo) +{ + PDEBUG + return (unsigned long) (p->cc * geo->heads * geo->sectors + + p->hh * geo->sectors + p->b); +} + +static char *fdasd_partition_type (char *str) +{ + PDEBUG + + if (strncmp("NATIVE", str, 6) == 0) + strcpy(str, "Linux native"); + else if (strncmp("NEW ", str, 6) == 0) + strcpy(str, "Linux native"); + else if (strncmp("SWAP ", str, 6) == 0) + strcpy(str, "Linux swap"); + else if (strncmp("RAID ", str, 6) == 0) + strcpy(str, "Linux Raid"); + else + strcpy(str, "unknown"); + + return str; +} + +/* + * initializes the anchor structure and allocates some + * memory for the labels + */ +void +fdasd_initialize_anchor (fdasd_anchor_t * anc) +{ + PDEBUG + int i; + volume_label_t *v; + partition_info_t *p = NULL; + partition_info_t *q = NULL; + + anc->devno = 0; + anc->dev_type = 0; + anc->used_partitions = 0; + + anc->silent = 0; + anc->verbose = 0; + anc->big_disk = 0; + anc->volid_specified = 0; + anc->config_specified = 0; + anc->auto_partition = 0; + anc->devname_specified = 0; + anc->print_table = 0; + + anc->option_reuse = 0; + anc->option_recreate = 0; + + anc->vlabel_changed = 0; + anc->vtoc_changed = 0; + anc->blksize = 0; + anc->fspace_trk = 0; + anc->label_pos = 0; + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + bzero(anc->confdata, sizeof(config_data_t)); + + anc->f4 = malloc(sizeof(format4_label_t)); + if (anc->f4 == NULL) + fdasd_error(anc, malloc_failed, + "FMT4 DSCB memory allocation failed."); + + anc->f5 = malloc(sizeof(format5_label_t)); + if (anc->f5 == NULL) + fdasd_error(anc, malloc_failed, + "FMT5 DSCB memory allocation failed."); + + anc->f7 = malloc(sizeof(format7_label_t)); + if (anc->f7 == NULL) + fdasd_error(anc, malloc_failed, + "FMT7 DSCB memory allocation failed."); + + bzero(anc->f4, sizeof(format4_label_t)); + bzero(anc->f5, sizeof(format5_label_t)); + bzero(anc->f7, sizeof(format7_label_t)); + + v = malloc(sizeof(volume_label_t)); + if (v == NULL) + fdasd_error(anc, malloc_failed, + "Volume label memory allocation failed."); + bzero(v, sizeof(volume_label_t)); + anc->vlabel = v; + + for (i=1; i<=USABLE_PARTITIONS; i++) { + p = malloc(sizeof(partition_info_t)); + if (p == NULL) + fdasd_error(anc, malloc_failed, + "Partition info memory allocation failed."); + p->used = 0x00; + p->len_trk = 0; + p->start_trk = 0; + p->fspace_trk = 0; + p->type = 0; + + /* add p to double pointered list */ + if (i == 1) { + anc->first = p; + p->prev = NULL; + } else if (i == USABLE_PARTITIONS) { + anc->last = p; + p->next = NULL; + p->prev = q; + q->next = p; + } else { + p->prev = q; + q->next = p; + } + + p->f1 = malloc(sizeof(format1_label_t)); + if (p->f1 == NULL) + fdasd_error(anc, malloc_failed, + "FMT1 DSCB memory allocation failed."); + bzero(p->f1, sizeof(format1_label_t)); + + q = p; + } +} + +/* + * call IOCTL to re-read the partition table + */ +static void +fdasd_reread_partition_table (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + char str[LINE_LENGTH]; + int f; + + if (ioctl (fd, BLKRRPART, NULL) != 0) + fdasd_error (anc, unable_to_ioctl, "Error while rereading " + "partition table.\nPlease reboot!"); +} + +/* + * writes all changes to dasd + */ +static void +fdasd_write_vtoc_labels (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + partition_info_t *p; + unsigned long b; + char dsno[6], s1[7], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + + b = (cchhb2blk (&anc->vlabel->vtoc, &anc->geo) - 1) * anc->blksize; + if (b <= 0) + fdasd_error (anc, vlabel_corrupted, ""); + + /* write FMT4 DSCB */ + vtoc_write_label (fd, b, NULL, anc->f4, NULL, NULL); + + /* write FMT5 DSCB */ + b += anc->blksize; + vtoc_write_label (fd, b, NULL, NULL, anc->f5, NULL); + + /* write FMT7 DSCB */ + if (anc->big_disk) { + b += anc->blksize; + vtoc_write_label (fd, b, NULL, NULL, NULL, anc->f7); + } + + /* loop over all FMT1 DSCBs */ + p = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + b += anc->blksize; + + if (p->used != 0x01) { + vtoc_write_label (fd, b, p->f1, NULL, NULL, NULL); + continue; + } + + strncpy (p->f1->DS1DSSN, anc->vlabel->volid, 6); + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + c1 = ch + 7; + + if (getdsn (anc, i) > -1) { + /* re-use the existing data set name */ + c2 = strchr (c1, '.'); + if (c2 != NULL) + strncpy (s2, c2, 31); + else + fdasd_error (anc, dsname_corrupted, ""); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + s1[6] = ' '; + strncpy (c1, s1, 7); + c1 = strchr (ch, ' '); + strncpy (c1, s2, 31); + } else { + /* create a new data set name */ + while (getpos (anc, k) > -1) + k++; + + setpos (anc, k, i); + + strncpy (s2, ch, 44); + s2[44] = 0; + vtoc_ebcdic_dec (s2, s2, 44); + + strncpy (ch, "LINUX.V " " ", 44); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + strncpy (c1, s1, 6); + + c1 = strchr (ch, ' '); + strncpy (c1, ".PART", 5); + c1 += 5; + + sprintf (dsno, "%04d.", k + 1); + strncpy (c1, dsno, 5); + + c1 += 5; + switch(p->type) { + case PARTITION_LINUX_LVM: + strncpy(c1, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + strncpy(c1, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + strncpy(c1, PART_TYPE_SWAP, 6); + break; + default: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + } + } + + vtoc_ebcdic_enc (ch, ch, 44); + + vtoc_write_label (fd, b, p->f1, NULL, NULL, NULL); + p = p->next; + } +} + +/* + * writes all changes to dasd + */ +int +fdasd_write_labels (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + if (anc->vlabel_changed) + vtoc_write_volume_label (fd, anc->label_pos, anc->vlabel); + + if (anc->vtoc_changed) + fdasd_write_vtoc_labels (anc, fd); + + return 1; +} + +/* + * writes all changes to dasd + */ +int +fdasd_prepare_labels (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + partition_info_t *p = anc->first; + char dsno[6], s1[7], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + + /* loop over all FMT1 DSCBs */ + p = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + strncpy (p->f1->DS1DSSN, anc->vlabel->volid, 6); + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + c1 = ch + 7; + + if (getdsn (anc, i) > -1) { + /* re-use the existing data set name */ + c2 = strchr (c1, '.'); + if (c2 != NULL) + strncpy (s2, c2, 31); + else + fdasd_error (anc, dsname_corrupted, ""); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + s1[6] = ' '; + strncpy (c1, s1, 7); + c1 = strchr (ch, ' '); + strncpy (c1, s2, 31); + } else { + /* create a new data set name */ + while (getpos (anc, k) > -1) + k++; + + setpos (anc, k, i); + + strncpy (s2, ch, 44); + s2[44] = 0; + vtoc_ebcdic_dec (s2, s2, 44); + + strncpy (ch, "LINUX.V " " ", 44); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + strncpy (c1, s1, 6); + + c1 = strchr (ch, ' '); + strncpy (c1, ".PART", 5); + c1 += 5; + + sprintf (dsno, "%04d.", k + 1); + strncpy (c1, dsno, 5); + + c1 += 5; + switch(p->type) { + case PARTITION_LINUX_LVM: + strncpy(c1, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + strncpy(c1, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + strncpy(c1, PART_TYPE_SWAP, 6); + break; + default: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + } + } + + vtoc_ebcdic_enc (ch, ch, 44); + p = p->next; + } + + return 1; +} + +void +fdasd_recreate_vtoc (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *p = anc->first; + int i; + + vtoc_init_format4_label(anc->f4, + USABLE_PARTITIONS, + anc->geo.cylinders, + anc->geo.heads, + anc->geo.sectors, + anc->blksize, + anc->dev_type); + + vtoc_init_format5_label(anc->f5); + vtoc_init_format7_label(anc->f7); + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, + '+', anc->verbose, + FIRST_USABLE_TRK, + anc->geo.cylinders * anc->geo.heads - 1, + anc->geo.cylinders, anc->geo.heads); + + for (i = 0; i < USABLE_PARTITIONS; i++) { + bzero(p->f1, sizeof(format1_label_t)); + p->used = 0x00; + p->start_trk = 0; + p->end_trk = 0; + p->len_trk = 0; + p->fspace_trk = 0; + p->type = 0; + p = p->next; + } + + anc->used_partitions = 0; + anc->fspace_trk = anc->geo.cylinders * anc->geo.heads - FIRST_USABLE_TRK; + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + anc->vtoc_changed++; +} + +/* + * changes the volume serial + */ +static void +fdasd_change_volser (fdasd_anchor_t *anc, char *line_ptr) +{ + PDEBUG + char str[10]; + + if (strcmp(line_ptr, "") != 0) { + int i; + + /* fill with blanks if necessary and remove the linebreak */ + i = strlen(line_ptr); + if (i <= 6) + strncpy(line_ptr + i - 1, " ", 6); + + strncpy(str, line_ptr, 6); + + for (i=0; i<6; i++) str[i] = toupper(str[i]); + str[6] = 0x00; + + fdasd_check_volser (str, anc->devno); + vtoc_volume_label_set_volser(anc->vlabel, str); + + vtoc_set_cchhb(&anc->vlabel->vtoc, 0x0000, 0x0001, 0x01); + anc->vlabel_changed++; + anc->vtoc_changed++; + } +} + +/* + * sets some important partition data + * (like used, start_trk, end_trk, len_trk) + * by calculating these values with the + * information provided in the labels + */ +static void +fdasd_update_partition_info (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *q = NULL, *p = anc->first; + unsigned int h = anc->geo.heads; + unsigned long max = anc->geo.cylinders * h - 1; + int i; + char *ch; + + anc->used_partitions = anc->geo.sectors - 2 - anc->f4->DS4DSREC; + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + if (p->f1->DS1FMTID != 0xf1) { + if (i == 1) + /* there is no partition at all */ + anc->fspace_trk = max - FIRST_USABLE_TRK + 1; + else + /* previous partition was the last one */ + q->fspace_trk = max - q->end_trk; + break; + } + + /* this is a valid format 1 label */ + p->used = 0x01; + p->start_trk = p->f1->DS1EXT1.llimit.cc * h + p->f1->DS1EXT1.llimit.hh; + p->end_trk = p->f1->DS1EXT1.ulimit.cc * h + p->f1->DS1EXT1.ulimit.hh; + p->len_trk = p->end_trk - p->start_trk + 1; + + if (i == 1) { + /* first partition, there is at least one */ + anc->fspace_trk = p->start_trk - FIRST_USABLE_TRK; + } else { + if (i == USABLE_PARTITIONS) + /* last possible partition */ + p->fspace_trk = max - p->end_trk; + + /* set free space values of previous partition */ + q->fspace_trk = p->start_trk - q->end_trk - 1; + } + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + if (strstr(ch, PART_TYPE_LVM)) + p->type = PARTITION_LINUX_LVM; + else if (strstr(ch, PART_TYPE_RAID)) + p->type = PARTITION_LINUX_RAID; + else if (strstr(ch, PART_TYPE_NATIVE)) + p->type = PARTITION_LINUX; + else if (strstr(ch, PART_TYPE_SWAP)) + p->type = PARTITION_LINUX_SWAP; + else + p->type = PARTITION_LINUX; + vtoc_ebcdic_enc (ch, ch, 44); + + q = p; + p = p->next; + } +} + +/* + * reorganizes all FMT1s, after that all used FMT1s should be right in + * front of all unused FMT1s + */ +static void +fdasd_reorganize_FMT1s (fdasd_anchor_t *anc) +{ + PDEBUG + int i, j; + format1_label_t *ltmp; + partition_info_t *ptmp; + + for (i=1; i<=USABLE_PARTITIONS - 1; i++) { + ptmp = anc->first; + + for (j=1; j<=USABLE_PARTITIONS - i; j++) { + if (ptmp->f1->DS1FMTID < ptmp->next->f1->DS1FMTID) { + ltmp = ptmp->f1; + ptmp->f1 = ptmp->next->f1; + ptmp->next->f1 = ltmp; + } + + ptmp=ptmp->next; + } + } +} + +static void +fdasd_process_valid_vtoc (fdasd_anchor_t * anc, unsigned long b, int fd) +{ + PDEBUG + int f5_counter = 0, f7_counter = 0, f1_counter = 0, oldfmt = 0; + int i, n, f1size = sizeof (format1_label_t); + partition_info_t *p = anc->first; + format1_label_t q; + char s[5], *ch; + + b += anc->blksize; + + for (i = 1; i <= anc->geo.sectors; i++) { + bzero (&q, f1size); + vtoc_read_label (fd, b, &q, NULL, NULL, NULL); + + switch (q.DS1FMTID) { + case 0xf1: + if (p == NULL) + break; + memcpy (p->f1, &q, f1size); + + n = -1; + vtoc_ebcdic_dec (p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr (p->f1->DS1DSNAM, "PART"); + if (ch != NULL) { + strncpy (s, ch + 4, 4); + s[4] = '\0'; + n = atoi (s) - 1; + } + + vtoc_ebcdic_enc (p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + + /* this dasd has data set names 0000-0002 + but we use now 0001-0003 */ + if (n == -1) + oldfmt++; + + if (((oldfmt == 0) && (n < 0)) || (n >= USABLE_PARTITIONS)) { + /* no op */ + } else { + if (oldfmt) { + /* correct +1 */ + setpos (anc, n + 1, f1_counter); + } else { + setpos (anc, n, f1_counter); + } + } + + p = p->next; + f1_counter++; + break; + case 0xf5: + memcpy (anc->f5, &q, f1size); + f5_counter++; + break; + case 0xf7: + if (f7_counter == 0) + memcpy (anc->f7, &q, f1size); + f7_counter++; + break; + } + + b += anc->blksize; + } + + if (oldfmt > 0) { + /* this is the old format PART0000 - PART0002 */ + anc->vtoc_changed++; + } + + if ((f5_counter == 0) || (anc->big_disk)) + vtoc_init_format5_label (anc->f5); + + if (f7_counter == 0) + vtoc_init_format7_label (anc->f7); + + fdasd_reorganize_FMT1s (anc); + fdasd_update_partition_info (anc); +} + +static int +fdasd_valid_vtoc_pointer(fdasd_anchor_t *anc, unsigned long b, int fd) +{ + PDEBUG + char str[LINE_LENGTH]; + + /* VOL1 label contains valid VTOC pointer */ + vtoc_read_label (fd, b, NULL, anc->f4, NULL, NULL); + + if (anc->f4->DS4IDFMT != 0xf4) { + if (strncmp(anc->vlabel->volkey,vtoc_ebcdic_enc("LNX1",str,4),4) == 0) + return 0; + fdasd_error(anc, wrong_disk_format, "Invalid VTOC"); + } else { + fdasd_process_valid_vtoc (anc, b, fd); + } + + return 0; +} + +/* + * check the dasd for a volume label + */ +int +fdasd_check_volume (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + volume_label_t *v = anc->vlabel; + unsigned long b = -1; + char str[LINE_LENGTH]; + + vtoc_read_volume_label (fd, anc->label_pos, v); + + if (strncmp(v->vollbl, vtoc_ebcdic_enc ("VOL1", str, 4), 4) == 0) { + /* found VOL1 volume label */ + b = (cchhb2blk (&v->vtoc, &anc->geo) - 1) * anc->blksize; + + if (b > 0) { + int rc; + rc = fdasd_valid_vtoc_pointer (anc, b, fd); + + if (rc < 0) + return 1; + else + return 0; + } else { + return 1; + } + } else if (strncmp (v->volkey, vtoc_ebcdic_enc ("LNX1", str, 4), 4) == 0) { + return 0; + } + + return 1; +} + +/* + * checks the current API version with the API version of the dasd driver + */ +void +fdasd_check_api_version (fdasd_anchor_t *anc, int f) +{ + PDEBUG + int api; + char s[LINE_LENGTH]; + + if (ioctl(f, DASDAPIVER, &api) != 0) + fdasd_error(anc, unable_to_ioctl, "Could not retrieve API version."); + + if (api != DASD_MIN_API_VERSION) { + sprintf(s, "The current API version '%d' doesn't " \ + "match dasd driver API version " \ + "'%d'!", api, DASD_MIN_API_VERSION); + fdasd_error(anc, api_version_mismatch, s); + } +} + +/* + * reads dasd geometry data + */ +void +fdasd_get_geometry (fdasd_anchor_t *anc, int f) +{ + PDEBUG + int blksize = 0; + dasd_information_t dasd_info; + char s[LINE_LENGTH]; + + if (ioctl(f, HDIO_GETGEO, &anc->geo) != 0) + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve disk geometry information."); + + if (ioctl(f, BLKSSZGET, &blksize) != 0) + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve blocksize information."); + + /* get disk type */ + if (ioctl(f, BIODASDINFO, &dasd_info) != 0) + fdasd_error(anc, unable_to_ioctl, + "Could not retrieve disk information."); + + if (strncmp(dasd_info.type, "ECKD", 4) != 0) { + sprintf(s, "This is not an ECKD disk! This disk type " \ + "is not supported!"); + fdasd_error(anc,wrong_disk_type, s); + } + + anc->dev_type = dasd_info.dev_type; + anc->blksize = blksize; + anc->label_pos = dasd_info.label_block * blksize; + anc->devno = dasd_info.devno; + anc->fspace_trk = anc->geo.cylinders * anc->geo.heads - FIRST_USABLE_TRK; +} + +/* + * returns unused partition info pointer if there + * is a free partition, otherwise NULL + */ +static partition_info_t * +fdasd_get_empty_f1_label (fdasd_anchor_t * anc) +{ + PDEBUG + if (anc->used_partitions < USABLE_PARTITIONS) + return anc->last; + else + return NULL; +} + +/* + * asks for and sets some important partition data + */ +static int +fdasd_get_partition_data (fdasd_anchor_t *anc, extent_t *part_extent, + partition_info_t *p, unsigned int *start_ptr, + unsigned int *stop_ptr) +{ + PDEBUG + unsigned int limit, cc, hh; + cchh_t llimit, ulimit; + partition_info_t *q; + char mesg[48]; + u_int8_t b1, b2; + u_int16_t c, h; + unsigned int start = *start_ptr, stop = *stop_ptr; + int i; + char *ch; + + if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED) + c = anc->f4->DS4DEVCT.DS4DSCYL - (u_int16_t) anc->f4->DS4DEVAC; + else + c = anc->f4->DS4DEVCT.DS4DSCYL; + + h = anc->f4->DS4DEVCT.DS4DSTRK; + limit = (h * c - 1); + + /* check start value from user */ + q = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + if ( q->next == NULL ) + break; + + if (start >= q->start_trk && start <= q->end_trk) { + /* start is within another partition */ + start = q->end_trk + 1; + + if (start > limit) { + start = FIRST_USABLE_TRK; + q = anc->first; + } + } + + if (start < q->start_trk) { + limit = q->start_trk - 1; + break; + } + + q = q->next; + } + + if (start == limit) + stop = start; + + /* update partition info */ + p->len_trk = stop - start + 1; + p->start_trk = start; + p->end_trk = stop; + + cc = start / anc->geo.heads; + hh = start - (cc * anc->geo.heads); + vtoc_set_cchh(&llimit, cc, hh); + + /* check for cylinder boundary */ + if (hh == 0) + b1 = 0x81; + else + b1 = 0x01; + + cc = stop / anc->geo.heads; + hh = stop - cc * anc->geo.heads; + vtoc_set_cchh(&ulimit, cc, hh); + + /* it is always the 1st extent */ + b2 = 0x00; + + vtoc_set_extent(part_extent, b1, b2, &llimit, &ulimit); + + *start_ptr = start; + *stop_ptr = stop; + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + + if (strstr(ch, PART_TYPE_LVM)) + p->type = PARTITION_LINUX_LVM; + else if (strstr(ch, PART_TYPE_RAID)) + p->type = PARTITION_LINUX_RAID; + else if (strstr(ch, PART_TYPE_NATIVE)) + p->type = PARTITION_LINUX; + else if (strstr(ch, PART_TYPE_SWAP)) + p->type = PARTITION_LINUX_SWAP; + else + p->type = PARTITION_LINUX; + + vtoc_ebcdic_enc (ch, ch, 44); + + return 0; +} + +static void +fdasd_enqueue_new_partition (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *q = anc->first, *p = anc->last; + int i, k=0; + + for (i=1; i<USABLE_PARTITIONS; i++) { + if ((q->end_trk == 0) || (p->start_trk < q->start_trk)) { + break; + } else { + q = q->next; + k++; + } + } + + if (anc->first == q) + anc->first = p; + + if (p != q) { + anc->last->prev->next = NULL; + anc->last = anc->last->prev; + + p->next = q; + p->prev = q->prev; + q->prev = p; + + if (p->prev != NULL) + p->prev->next = p; + } + + p->used = 0x01; + p->type = PARTITION_LINUX; + + for (i=0; i<USABLE_PARTITIONS; i++) { + int j = getpos(anc, i); + if (j >= k) + setpos(anc, i, j + 1); + } + + /* update free-space counters */ + if (anc->first == p) { + /* partition is the first used partition */ + if (p->start_trk == FIRST_USABLE_TRK) { + /* partition starts right behind VTOC */ + p->fspace_trk = anc->fspace_trk - p->len_trk; + anc->fspace_trk = 0; + } else { + /* there is some space between VTOC and partition */ + p->fspace_trk = anc->fspace_trk - p->len_trk - p->start_trk + + FIRST_USABLE_TRK; + anc->fspace_trk = p->start_trk - FIRST_USABLE_TRK; + } + } else { + /* there are partitons in front of the new one */ + if (p->start_trk == p->prev->end_trk + 1) { + /* new partition is right behind the previous one */ + p->fspace_trk = p->prev->fspace_trk - p->len_trk; + p->prev->fspace_trk = 0; + } else { + /* there is some space between new and prev. part. */ + p->fspace_trk = p->prev->fspace_trk - p->len_trk + - p->start_trk + p->prev->end_trk + 1; + p->prev->fspace_trk = p->start_trk - p->prev->end_trk - 1; + } + } +} + +/* + * adds a new partition to the 'partition table' + */ +partition_info_t * +fdasd_add_partition (fdasd_anchor_t *anc, unsigned int start, + unsigned int stop) +{ + PDEBUG + cchhb_t hf1; + partition_info_t *p; + extent_t ext; + int i; + + PDEBUG; + + if ((p = fdasd_get_empty_f1_label(anc)) == NULL) { + PDEBUG; + return 0; + } + + PDEBUG; + if (fdasd_get_partition_data(anc, &ext, p, &start, &stop) != 0) + return 0; + + PDEBUG; + vtoc_init_format1_label(anc->vlabel->volid, anc->blksize, &ext, p->f1); + + PDEBUG; + fdasd_enqueue_new_partition(anc); + + PDEBUG; + anc->used_partitions += 1; + + i = anc->used_partitions + 2; + if (anc->big_disk) + i++; + PDEBUG; + + vtoc_set_cchhb(&hf1, VTOC_START_CC, VTOC_START_HH, i); + + vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); + + PDEBUG; + + start = ext.llimit.cc * anc->geo.heads + ext.llimit.hh; + stop = ext.ulimit.cc * anc->geo.heads + ext.ulimit.hh; + + PDEBUG; + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '-', anc->verbose, + start, stop, anc->geo.cylinders, anc->geo.heads); + + anc->vtoc_changed++; + + PDEBUG; + return p; +} + +/* vim:set tabstop=4 shiftwidth=4 softtabstop=4: */ |