diff options
Diffstat (limited to '')
-rw-r--r-- | libparted/labels/fdasd.c | 1442 |
1 files changed, 1442 insertions, 0 deletions
diff --git a/libparted/labels/fdasd.c b/libparted/labels/fdasd.c new file mode 100644 index 0000000..cee4d46 --- /dev/null +++ b/libparted/labels/fdasd.c @@ -0,0 +1,1442 @@ +/* + * 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 <config.h> +#include <arch/linux.h> +#include <parted/vtoc.h> +#include <parted/device.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 */ + +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 u_int32_t +get_usable_cylinders (fdasd_anchor_t *anc) +{ + u_int32_t cyl; + + /* large volume */ + if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL && + anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL) + return anc->f4->DS4DCYL; + /* normal volume */ + if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED) + cyl = anc->f4->DS4DEVCT.DS4DSCYL - + (u_int16_t) anc->f4->DS4DEVAC; + else + cyl = anc->f4->DS4DEVCT.DS4DSCYL; + return cyl; +} + +static void +get_addr_of_highest_f1_f8_label (fdasd_anchor_t *anc, cchhb_t *addr) +{ + + u_int8_t record; + /* We have to count the follwing labels: + * one format 4 + * one format 5 + * format 7 only if we have moren then BIG_DISK_SIZE tracks + * one for each format 1 or format 8 label == one for each partition + * one for each format 9 label before the last format 8 + * We assume that all partitions use format 8 labels when + * anc->formatted_cylinders > LV_COMPAT_CYL + * Note: Record zero is special, so block 0 on our disk is record 1! + */ + + record = anc->used_partitions + 2; + if (anc->big_disk) + record++; + if (anc->formatted_cylinders > LV_COMPAT_CYL) + record += anc->used_partitions - 1; + vtoc_set_cchhb(addr, VTOC_START_CC, VTOC_START_HH, record); +} + +void +fdasd_cleanup (fdasd_anchor_t *anchor) +{ + PDEBUG + int i; + partition_info_t *part_info, *next; + + if (anchor == NULL) + return; + + free(anchor->f4); + free(anchor->f5); + free(anchor->f7); + free(anchor->vlabel); + + part_info = anchor->first; + for (i = 1; i <= USABLE_PARTITIONS && part_info != NULL; i++) { + next = part_info->next; + free(part_info->f1); + free(part_info); + part_info = next; + } +} + +static void +fdasd_error (fdasd_anchor_t *anc, enum fdasd_failure why, char const *str) +{ + PDEBUG + char error[2*LINE_LENGTH], *message = error; + + switch (why) { + case unable_to_open_disk: + sprintf(error, "fdasd: %s -- %s\n", _("open error"), str); + break; + case unable_to_seek_disk: + sprintf(error, "fdasd: %s -- %s\n", _("seek error"), str); + break; + case unable_to_read_disk: + sprintf(error, "fdasd: %s -- %s\n", _("read error"), str); + break; + case read_only_disk: + sprintf(error, "fdasd: %s -- %s\n", _("write error"), str); + break; + case unable_to_ioctl: + sprintf(error, "fdasd: %s -- %s\n", _("ioctl() error"), str); + break; + case api_version_mismatch: + sprintf(error, "fdasd: %s -- %s\n", + _("API version mismatch"), str); + break; + case wrong_disk_type: + sprintf(error, "fdasd: %s -- %s\n", + _("Unsupported disk type"), str); + break; + case wrong_disk_format: + sprintf(error, "fdasd: %s -- %s\n", + _("Unsupported disk format"), str); + break; + case disk_in_use: + sprintf(error, "fdasd: %s -- %s\n", + _("Disk is in use"), str); + break; + case config_syntax_error: + sprintf(error, "fdasd: %s -- %s\n", + _("Syntax error in config file"), str); + break; + case vlabel_corrupted: + sprintf(error, "fdasd: %s -- %s\n", + _("Volume label is corrupted"), str); + break; + case dsname_corrupted: + sprintf(error, "fdasd: %s -- %s\n", + _("A data set name is corrupted"), str); + break; + case malloc_failed: + sprintf(error, "fdasd: %s -- %s\n", + _("Memory allocation failed"), str); + break; + case device_verification_failed: + sprintf(error, "fdasd: %s -- %s\n", + _("Device verification failed"), + _("The specified device is not a valid DASD device")); + break; + case volser_not_found: + sprintf(error, "fdasd: %s -- %s\n", _("VOLSER not found on device"), str); + break; + default: + sprintf(error, "fdasd: %s: %s\n", _("Fatal error"), str); + } + + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, message); +} + +/* + * 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; + + bzero(anc, sizeof(fdasd_anchor_t)); + + 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."); + + anc->f5 = malloc(sizeof(format5_label_t)); + if (anc->f5 == NULL) + fdasd_error(anc, malloc_failed, "FMT5 DSCB."); + + anc->f7 = malloc(sizeof(format7_label_t)); + if (anc->f7 == NULL) + fdasd_error(anc, malloc_failed, "FMT7 DSCB."); + + /* template for all format 9 labels */ + anc->f9 = malloc(sizeof(format9_label_t)); + if (anc->f9 == NULL) + fdasd_error(anc, malloc_failed, "FMT9 DSCB."); + + bzero(anc->f4, sizeof(format4_label_t)); + bzero(anc->f5, sizeof(format5_label_t)); + bzero(anc->f7, sizeof(format7_label_t)); + bzero(anc->f9, sizeof(format9_label_t)); + vtoc_init_format9_label(anc->f9); + + v = malloc(sizeof(volume_label_t)); + if (v == NULL) + fdasd_error(anc, malloc_failed, + _("No room for volume label.")); + 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, + _("No room for partition info.")); + bzero(p, sizeof(partition_info_t)); + + /* add p to double pointered list */ + if (i == 1) { + anc->first = p; + } else if (i == USABLE_PARTITIONS) { + anc->last = p; + 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."); + bzero(p->f1, sizeof(format1_label_t)); + + q = p; + } + anc->hw_cylinders = 0; + anc->formatted_cylinders = 0; + anc->is_file = 0; +} + +/* + * 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, maxblk; + char dsno[6], s1[VOLSER_LENGTH + 1], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + cchhb_t f9addr; + format1_label_t emptyf1; + + b = (cchhb2blk (&anc->vlabel->vtoc, &anc->geo) - 1) * anc->blksize; + if (b <= 0) + fdasd_error (anc, vlabel_corrupted, ""); + maxblk = b + anc->blksize * 9; /* f4+f5+f7+3*f8+3*f9 */ + + /* write FMT4 DSCB */ + vtoc_write_label (fd, b, NULL, anc->f4, NULL, NULL, NULL); + b += anc->blksize; + + /* write FMT5 DSCB */ + vtoc_write_label (fd, b, NULL, NULL, anc->f5, NULL, NULL); + b += anc->blksize; + + /* write FMT7 DSCB */ + if (anc->big_disk) { + vtoc_write_label (fd, b, NULL, NULL, NULL, anc->f7, NULL); + b += anc->blksize; + } + + /* loop over all partitions (format 1 or format 8 DCB) */ + for (p = anc->first; p != NULL; p = p->next) { + + if (p->used != 0x01) { + continue; + } + + 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) > -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-1); + + 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); + + if (p->f1->DS1FMTID == 0xf8 ) { + /* Now as we know where which label will be written, we + * can add the address of the format 9 label to the + * format 8 label. The f9 record will be written to the + * block after the current blk. Remember: records are of + * by one, so we have to add 2 and not just one. + */ + vtoc_set_cchhb(&f9addr, VTOC_START_CC, VTOC_START_HH, + ((b / anc->blksize) % anc->geo.sectors) + 2); + vtoc_update_format8_label(&f9addr, p->f1); + vtoc_write_label(fd, b, p->f1, NULL, NULL, NULL, NULL); + b += anc->blksize; + vtoc_write_label(fd, b, NULL, NULL, NULL, NULL, + anc->f9); + b += anc->blksize; + } else { + vtoc_write_label(fd, b, p->f1, NULL, NULL, NULL, NULL); + b += anc->blksize; + } + } + + /* write empty labels to the rest of the blocks */ + bzero(&emptyf1, sizeof(emptyf1)); + while (b < maxblk) { + vtoc_write_label(fd, b, &emptyf1, NULL, NULL, NULL, NULL); + b += anc->blksize; + } +} + +/* + * 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; + char dsno[6], s1[7], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + + /* loop over all partitions (format 1 or format 8 DCB) */ + for (p = anc->first; p != NULL; p = p->next) { + + if (p->used != 0x01) { + continue; + } + + 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) > -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-1); + + 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); + } + + 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, + anc->geo.cylinders, + anc->formatted_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->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_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->formatted_cylinders * anc->geo.heads - + FIRST_USABLE_TRK; + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + anc->vtoc_changed++; +} + + /* + * initialize the VOL1 volume label + */ +static void +fdasd_init_volume_label(fdasd_anchor_t *anc, int fd) +{ + volume_label_t *vlabel = anc->vlabel; + + vtoc_volume_label_init(vlabel); + vtoc_volume_label_set_key(vlabel, "VOL1"); + vtoc_volume_label_set_label(vlabel, "VOL1"); + + vtoc_set_cchhb(&vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); +} + + +/* + * 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 long max = anc->formatted_cylinders * anc->geo.heads - 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 && + p->f1->DS1FMTID != 0xf8) { + 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 = cchh2trk(&p->f1->DS1EXT1.llimit, &anc->geo); + p->end_trk = cchh2trk(&p->f1->DS1EXT1.ulimit, &anc->geo); + 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; + + if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL && + anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL) + anc->formatted_cylinders = anc->f4->DS4DCYL; + else + anc->formatted_cylinders = anc->f4->DS4DEVCT.DS4DSCYL; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads - + FIRST_USABLE_TRK; + 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: + case 0xf8: + 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; + case 0xf9: + /* each format 8 lable has an associated + * format 9 lable, but they are of no further + * use to us. + */ + 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 void +fdasd_invalid_vtoc_pointer(fdasd_anchor_t *anc) +{ + PDEBUG + anc->formatted_cylinders = anc->hw_cylinders; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads + - FIRST_USABLE_TRK; + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, anc->formatted_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->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + + vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); + anc->vtoc_changed++; + anc->vlabel_changed++; +} + +/* + * we have a invalid FMT4 DSCB and therefore we will re-create the VTOC + */ +static void +fdasd_process_invalid_vtoc(fdasd_anchor_t *anc) +{ + anc->formatted_cylinders = anc->hw_cylinders; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads + - FIRST_USABLE_TRK; + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, anc->formatted_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->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + + anc->vtoc_changed++; +} + + +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) { + fdasd_process_valid_vtoc (anc, b, fd); + return 0; + } else { + fdasd_process_invalid_vtoc(anc); + } + if (strncmp(anc->vlabel->volkey, vtoc_ebcdic_enc("LNX1",str,4),4) == 0 || + strncmp(anc->vlabel->volkey, vtoc_ebcdic_enc("CMS1",str,4),4) == 0) + return 0; + + fdasd_error(anc, wrong_disk_format, _("Invalid VTOC.")); + return 1; +} + +/* + * check the dasd for a volume label + */ +int +fdasd_check_volume (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + volume_label_t *v = anc->vlabel; + long long b = -1; + char str[LINE_LENGTH]; + + memset(v, 0, sizeof(volume_label_t)); + vtoc_read_volume_label (fd, anc->label_pos, v); + + if (strncmp(v->vollbl, vtoc_ebcdic_enc ("VOL1", str, 4), 4) == 0) { + if (anc->FBA_layout != 1 ) { + /* 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 { + fdasd_invalid_vtoc_pointer(anc); + } + } + } else if (strncmp (v->volkey, vtoc_ebcdic_enc ("LNX1", str, 4), 4) == 0 || + strncmp (v->volkey, vtoc_ebcdic_enc ("CMS1", str, 4), 4) == 0) { + return 0; + } else if (anc->FBA_layout == 1) { + /* Some times LDL formatted disks does not + contain any volume label */ + return 1; + } else if (! anc->is_file) { + /* didn't find VOL1 volume label */ + anc->formatted_cylinders = anc->hw_cylinders; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads + - FIRST_USABLE_TRK; + + fdasd_init_volume_label(anc, fd); + + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, anc->formatted_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->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + 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[2*LINE_LENGTH]; + + struct stat st; + if (fstat (f, &st) == 0 && S_ISREG (st.st_mode)) { + /* skip these tests when F is a regular file. */ + } + else { + 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); + } + } +} + +/* + * The following two functions match those in the DASD ECKD device driver. + * They are used to compute how many records of a given size can be stored + * in one track. + */ +static unsigned int ceil_quot(unsigned int d1, unsigned int d2) +{ + return (d1 + (d2 - 1)) / d2; +} + +/* kl: key length, dl: data length */ +static unsigned int recs_per_track(unsigned short dev_type, unsigned int kl, + unsigned int dl) +{ + unsigned int dn, kn; + + switch (dev_type) { + case DASD_3380_TYPE: + if (kl) + return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + + ceil_quot(dl + 12, 32)); + else + return 1499 / (15 + ceil_quot(dl + 12, 32)); + case DASD_3390_TYPE: + dn = ceil_quot(dl + 6, 232) + 1; + if (kl) { + kn = ceil_quot(kl + 6, 232) + 1; + return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + + 9 + ceil_quot(dl + 6 * dn, 34)); + } else + return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); + case DASD_9345_TYPE: + dn = ceil_quot(dl + 6, 232) + 1; + if (kl) { + kn = ceil_quot(kl + 6, 232) + 1; + return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + + ceil_quot(dl + 6 * dn, 34)); + } else + return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); + } + return 0; +} + +/* + * Verify that number of tracks (heads) per cylinder and number of + * sectors per track match the expected values for a given device type + * and block size. + * Returns 1 for a valid match and 0 otherwise. + */ +static int fdasd_verify_geometry(unsigned short dev_type, int blksize, + struct fdasd_hd_geometry *geometry) +{ + unsigned int expected_sectors; + if (geometry->heads != 15) + return 0; + expected_sectors = recs_per_track(dev_type, 0, blksize); + if (geometry->sectors == expected_sectors) + return 1; + return 0; +} + +/* + * reads dasd geometry data + */ +int +fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int f) +{ + PDEBUG + int blksize = 0; + dasd_information_t dasd_info; + struct dasd_eckd_characteristics *characteristics; + unsigned long long size_in_bytes; + + /* We can't get geometry from a regular file, + so simulate something usable, for the sake of testing. */ + struct stat st; + if (fstat (f, &st) == 0 && S_ISREG (st.st_mode)) { + PedSector n_sectors = st.st_size / dev->sector_size; + anc->geo.heads = 15; + anc->geo.sectors = 12; + anc->geo.cylinders + = (n_sectors / (anc->geo.heads * anc->geo.sectors + * (dev->sector_size / dev->phys_sector_size))); + anc->geo.start = 0; + blksize = 4096; + memcpy (dasd_info.type, "ECKD", 4); + dasd_info.dev_type = 13200; + dasd_info.label_block = 2; + dasd_info.devno = 513; + dasd_info.label_block = 2; + dasd_info.FBA_layout = 0; + anc->hw_cylinders = ((st.st_size / blksize) / anc->geo.sectors) / + anc->geo.heads; + anc->is_file = 1; + } else { + if (ioctl(f, BLKGETSIZE64, &size_in_bytes) != 0) { + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve disk size.")); + goto error; + } + + if (ioctl(f, HDIO_GETGEO, &anc->geo) != 0 || + anc->geo.heads == 0 || + anc->geo.sectors == 0 || + anc->geo.cylinders == 0 ) { + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve disk geometry information.")); + goto error; + } + + if (ioctl(f, BLKSSZGET, &blksize) != 0) { + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve blocksize information.")); + goto error; + } + + /* get disk type */ + if (ioctl(f, BIODASDINFO, &dasd_info) != 0) { + /* verify that the geometry matches a 3390 DASD */ + if (!fdasd_verify_geometry(DASD_3390_TYPE, blksize, + &anc->geo)) { + fdasd_error(anc, wrong_disk_type, + _("Disk geometry does not match a " \ + "DASD device of type 3390.")); + goto error; + } + anc->dev_type = DASD_3390_TYPE; + anc->hw_cylinders = + size_in_bytes / (blksize * anc->geo.heads * anc->geo.sectors); + /* The VOL1 label on a CDL formatted ECKD DASD is in block 2 + * It will be verified later, if this position actually holds a + * valid label record. + */ + anc->label_pos = 2 * blksize; + /* A devno 0 is actually a valid devno, which could exist + * in the system. Since we use this number only to create + * a default volume serial, there is no serious conflict. + */ + anc->devno = 0; + } else { + characteristics = (struct dasd_eckd_characteristics *) + &dasd_info.characteristics; + if (characteristics->no_cyl == LV_COMPAT_CYL && + characteristics->long_no_cyl) + anc->hw_cylinders = characteristics->long_no_cyl; + else + anc->hw_cylinders = characteristics->no_cyl; + anc->dev_type = dasd_info.dev_type; + anc->label_pos = dasd_info.label_block * blksize; + anc->devno = dasd_info.devno; + anc->label_block = dasd_info.label_block; + anc->FBA_layout = dasd_info.FBA_layout; + } + + anc->is_file = 0; + } + + anc->blksize = blksize; + return 1; + + error: + return 0; +} + +/* + * 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; + u_int32_t cc, c; + u_int16_t hh, h; + cchh_t llimit, ulimit; + partition_info_t *q; + u_int8_t b1, b2; + unsigned int start = *start_ptr, stop = *stop_ptr; + int i; + char *ch; + + c = get_usable_cylinders(anc); + 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 partitions 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; + + 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; + + if (anc->formatted_cylinders > LV_COMPAT_CYL) { + vtoc_init_format8_label(anc->blksize, &ext, p->f1); + } else { + PDEBUG; + vtoc_init_format1_label(anc->blksize, &ext, p->f1); + } + + PDEBUG; + fdasd_enqueue_new_partition(anc); + + PDEBUG; + anc->used_partitions += 1; + + get_addr_of_highest_f1_f8_label(anc, &hf1); + vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); + + PDEBUG; + + start = cchh2trk(&ext.llimit, &anc->geo); + stop = cchh2trk(&ext.ulimit, &anc->geo); + + PDEBUG; + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '-', anc->verbose, + start, stop, anc->formatted_cylinders, anc->geo.heads); + + anc->vtoc_changed++; + + PDEBUG; + return p; +} + +/* + * Check for valid volume serial characters (max. 6) - remove invalid. + * If volser is empty, fill with default volser. + */ +void fdasd_check_volser (char *volser, int devno) +{ + int from, to; + + for (from = 0, to = 0; volser[from] && from < VOLSER_LENGTH; from++) { + + if ((volser[from] >= 0x23 && + volser[from] <= 0x25) || + (volser[from] >= 0x30 && + volser[from] <= 0x39) || + (volser[from] >= 0x40 && + volser[from] <= 0x5a) || + (volser[from] >= 0x61 && + volser[from] <= 0x7a)) + volser[to++] = toupper(volser[from]); + } + + volser[to] = 0x00; + + if (volser[0] == 0x00) + sprintf(volser, "0X%04x", devno); +} + +/* + * get volser from vtoc + */ +int fdasd_get_volser (fdasd_anchor_t *anc, char *volser, int fd) +{ + volume_label_t vlabel; + + vtoc_read_volume_label(fd, anc->label_pos, &vlabel); + vtoc_volume_label_get_volser(&vlabel, volser); + return 0; +} + +/* Changes the volume serial (menu option) + * + */ +void fdasd_change_volser (fdasd_anchor_t *anc, char *str) +{ + fdasd_check_volser(str, anc->devno); + vtoc_volume_label_set_volser(anc->vlabel, str); + + vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); + anc->vlabel_changed++; + anc->vtoc_changed++; +} + +/* + * re-create all VTOC labels, but use the partition information + * from existing VTOC + */ +void fdasd_reuse_vtoc (fdasd_anchor_t *anc) +{ + partition_info_t *part_info = anc->first; + struct fdasd_hd_geometry geo = anc->geo; + format1_label_t f1; + format4_label_t f4; + format5_label_t f5; + format7_label_t f7; + + vtoc_init_format4_label(&f4, geo.cylinders, anc->formatted_cylinders, + geo.heads, geo.sectors, + anc->blksize, anc->dev_type); + + /* reuse some FMT4 values */ + f4.DS4HPCHR = anc->f4->DS4HPCHR; + f4.DS4DSREC = anc->f4->DS4DSREC; + + /* re-initialize both free-space labels */ + vtoc_init_format5_label(&f5); + vtoc_init_format7_label(&f7); + + if (anc->fspace_trk > 0) + vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose, + FIRST_USABLE_TRK, + FIRST_USABLE_TRK + anc->fspace_trk - 1, + anc->formatted_cylinders, geo.heads); + + while (part_info != NULL) { + if (part_info->used != 0x01) { + part_info = part_info->next; + continue; + } + + if (anc->formatted_cylinders > LV_COMPAT_CYL) + vtoc_init_format8_label(anc->blksize, + &part_info->f1->DS1EXT1, &f1); + else + vtoc_init_format1_label(anc->blksize, + &part_info->f1->DS1EXT1, &f1); + + + strncpy(f1.DS1DSNAM, part_info->f1->DS1DSNAM, 44); + strncpy((char *)f1.DS1DSSN, (char *)part_info->f1->DS1DSSN, 6); + f1.DS1CREDT = part_info->f1->DS1CREDT; + + memcpy(part_info->f1, &f1, sizeof(format1_label_t)); + + if (part_info->fspace_trk > 0) + vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose, + part_info->end_trk + 1, + part_info->end_trk + + part_info->fspace_trk, + anc->formatted_cylinders, geo.heads); + + part_info = part_info->next; + } + + /* over-write old labels with new ones */ + memcpy(anc->f4, &f4, sizeof(format4_label_t)); + memcpy(anc->f5, &f5, sizeof(format5_label_t)); + memcpy(anc->f7, &f7, sizeof(format7_label_t)); + + anc->vtoc_changed++; + + return; +} + +/* vim:set tabstop=4 shiftwidth=4 softtabstop=4: */ |