| /* |
| * 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 <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 */ |
| |
| 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; |
| } |
| |
| 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); |
| } |
| |
| /* |
| * 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; |
| } |
| } |
| |
| /* |
| * 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++; |
| } |
| |
| /* |
| * 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; |
| 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: */ |