#include #include #include #include #include #include #include #include #include #include #include #include "hd.h" #include "install.h" #include "intl.h" #include "log.h" #include "newt.h" #include "run.h" #include "scsi.h" #include "windows.h" extern int testing; /* shame we can't get this from any header files */ #define BLKRRPART _IO(0x12,95) /* re-read partition table */ struct fdiskTag { int tag; int type; } ; #if defined(__sparc__) const struct fdiskTag fdiskTags[] = { { 0x05, PART_IGNORE }, { 0x82, PART_SWAP }, { 0x83, PART_EXT2 }, { 0, 0 }, }; #else const struct fdiskTag fdiskTags[] = { { 0x01, PART_DOS }, { 0x04, PART_DOS }, { 0x05, PART_IGNORE }, { 0x06, PART_DOS }, { 0x07, PART_HPFS }, { 0x0b, PART_FAT32 }, { 0x82, PART_SWAP }, { 0x83, PART_EXT2 }, { 0, 0 }, }; #endif #define MAX_NUM_DRIVES 40 static int findDrivesPresent(struct deviceInfo * drives, int * numPtr); static void parseLabelLine(char * device, char * start, int * numPartitions, struct partition partitions[15]) { char * str = start; char * chptr; char devbuf[2]; if (*str++ != ' ') return; if (*str++ != ' ') return; if (*str < 'a' || *str > 'h') return; if (*(str + 1) != ':') return; strcpy(partitions[*numPartitions].device, device); devbuf[0] = *str - 'a' + '1'; devbuf[1] = '\0'; strcat(partitions[*numPartitions].device, devbuf); str += 2; while (*str && isspace(*str)) str++; if (!*str) return; partitions[*numPartitions].size = strtol(str, &chptr, 10); partitions[*numPartitions].size /= 2; /* we want 1k blocks */ if (!chptr || !isspace(*chptr)) return; str = chptr; while (isspace(*str)) str++; if (!*str) return; partitions[*numPartitions].begin = strtol(str, &chptr, 10); if (!chptr || !isspace(*chptr)) return; str = chptr; partitions[*numPartitions].end = partitions[*numPartitions].size * 2 + partitions[*numPartitions].begin; while (isspace(*str)) str++; if (!*str) return; if (!strncmp(str, "ext2", 4)) { partitions[*numPartitions].type = PART_EXT2; strcpy(partitions[*numPartitions].tagName, _("Linux native")); } else if (!strncmp(str, "swap", 4)) { partitions[*numPartitions].type = PART_SWAP; strcpy(partitions[*numPartitions].tagName, _("Linux swap")); } else { partitions[*numPartitions].type = PART_OTHER; strcpy(partitions[*numPartitions].tagName, _("Other")); } partitions[*numPartitions].bootLabel = NULL; partitions[*numPartitions].defaultBoot = 0; logMessage("found partition %s", partitions[*numPartitions].device); (*numPartitions)++; } static void parseFdiskLine(char * device, char * start, int * numPartitions, struct partition partitions[15]) { int tag, i; char * str = start; char * chptr; if (*str != '/') return; if (strlen(str) < 60) return; /* grab the partition number */ str += 8; chptr = str; if (!isdigit(*chptr)) return; while (*chptr && isdigit(*chptr)) chptr++; if (!*chptr) return; *chptr = '\0'; strcpy(partitions[*numPartitions].device, device); strcat(partitions[*numPartitions].device, str); str = chptr + 1; /* go to the "Begin" field, skipping the '*' which marks a bootable partition and the 'u|r' fields which can occur on a Sun disklabel */ while (*str && (isspace(*str) || *str == '*' || *str == 'u' || *str == 'r')) str++; if (!*str) return; /* skip the Begin: field (but make sure it's a number */ if (!isdigit(*str)) return; while (isdigit(*str)) str++; if (!*str || !isspace(*str)) return; /* go to the first digit in the "Start" field */ while (isspace(*str)) str++; if (!*str) return; partitions[*numPartitions].begin = strtol(str, &chptr, 10); if (!chptr || !isspace(*chptr)) return; str = chptr; /* go to the first digit in the "End" field */ while (isspace(*str)) str++; if (!*str) return; partitions[*numPartitions].end = strtol(str, &chptr, 10); if (!chptr || !isspace(*chptr)) return; str = chptr; /* go to the first digit in the "Size" field */ while (isspace(*str)) str++; if (!*str) return; partitions[*numPartitions].size = strtol(str, &chptr, 10); if (!chptr || (*chptr != '-' && *chptr != '+' && !isspace(*chptr))) return; str = chptr + 1; /* go to the first digit in the "Tag" field */ while (isspace(*str)) str++; if (!*str) return; tag = strtol(str, &chptr, 16); partitions[*numPartitions].type = PART_OTHER; for (i = 0; fdiskTags[i].tag; i++) { if (fdiskTags[i].tag == tag) { partitions[*numPartitions].type = fdiskTags[i].type; break; } } str = chptr; while (isspace(*str)) str++; if (!*str) return; strcpy(partitions[*numPartitions].tagName, str); partitions[*numPartitions].bootLabel = NULL; partitions[*numPartitions].defaultBoot = 0; logMessage("found partition %s", partitions[*numPartitions].device); (*numPartitions)++; } static int findPartitions(char * hdname, int * numPartitions, struct partition partitions[15]) { char devBuf[20]; int fd; char * fdiskOutput; char * fdiskArgs[] = { NULL, NULL, NULL }; char * start, * end; int oldTesting; int len; char * cmd; int labelMode = 0; #if defined(__alpha__) unsigned short magic; unsigned int fdiskMagic = 0xAA55; #endif if (testing) cmd = "/sbin/fdisk"; else cmd = "/usr/bin/fdisk"; fdiskArgs[0] = cmd; *numPartitions = 0; /* don't bother with any of this if the main device doesn't exist or doesn't look like a hard drive */ sprintf(devBuf, "/tmp/%s", hdname); if (devMakeInode(hdname, devBuf)) { return 0; } fd = open(devBuf, O_RDONLY); if (fd < 0) { unlink(devBuf); return 0; } #if defined(__alpha__) /* is this a labeled disk? */ /* This test used to just look for the disklabel magic, but that got left around on fdisks drives much of the time. This check seems more reliable */ lseek(fd, 510, SEEK_SET); if (read(fd, &magic, sizeof(magic)) == sizeof(magic)) labelMode = (magic != fdiskMagic); if (labelMode) logMessage("/dev/%s is disklabeled", hdname); else logMessage("/dev/%s is fdisked", hdname); #endif close(fd); oldTesting = testing; testing = 0; fdiskArgs[1] = devBuf; if (labelMode) runProgramIO(RUN_NOLOG, cmd, fdiskArgs, "b\np\nq\n", &fdiskOutput); else runProgramIO(RUN_NOLOG, cmd, fdiskArgs, "p\nq\n", &fdiskOutput); unlink(devBuf); testing = oldTesting; start = fdiskOutput; len = strlen(start); while (((start - fdiskOutput) < len) && (end = strchr(start, '\n'))) { *end = '\0'; if (labelMode) parseLabelLine(hdname, start, numPartitions, partitions); else parseFdiskLine(hdname, start, numPartitions, partitions); start = end + 1; } free(fdiskOutput); return 0; } int findAllPartitions(struct deviceInfo * devices, struct partitionTable * table) { int numPartitions; int i; struct partition parts[16]; struct partition * allParts = NULL; int numAllParts = 0; if (!devices) { /* FIXME: memory leak for internal structures */ devices = alloca(sizeof(*devices) * MAX_NUM_DRIVES); if (findDrivesPresent(devices, NULL)) return INST_ERROR; } winStatus(30, 3, _("Hard Drives"), _("Scanning hard drives...")); for (i = 0; devices[i].deviceName; i++) { findPartitions(devices[i].deviceName, &numPartitions, parts); if (!numPartitions) continue; if (!numAllParts) { numAllParts = numPartitions; allParts = malloc(sizeof(*allParts) * numAllParts); memcpy(allParts, parts, sizeof(*allParts) * numAllParts); } else { allParts = realloc(allParts, sizeof(*allParts) * (numAllParts + numPartitions)); memcpy(allParts + numAllParts, parts, sizeof(*allParts) * numPartitions); numAllParts += numPartitions; } } table->count = numAllParts; table->parts = allParts; newtPopWindow(); return 0; } static int findDrivesPresent(struct deviceInfo * drives, int * numPtr) { struct deviceInfo * scsi = NULL, * ide = NULL; int num = 0; int i; /* FIXME: this results in memory leaks via the strings inside of the decice structures! */ if (scsiDeviceAvailable()) { if (scsiGetDevices(&scsi)) return INST_ERROR; } if (ideGetDevices(&ide)) return INST_ERROR; i = 0, num = 0; while (ide[i].deviceName) { if (ide[i].type == DEVICE_HD) drives[num++] = ide[i]; i++; } i = 0; while (scsi && scsi[i].deviceName) { if (scsi[i].type == DEVICE_HD) drives[num++] = scsi[i]; i++; } drives[num].deviceName = NULL; if (numPtr) *numPtr = num; return 0; } int getDriveList(char *** drives, int * num) { struct deviceInfo drivesPresent[MAX_NUM_DRIVES]; int rc; int i; if ((rc = findDrivesPresent(drivesPresent, num))) return rc; *drives = malloc(sizeof(char *) * (*num + 1)); for (i = 0; i < *num; i++) { (*drives)[i] = drivesPresent[i].deviceName; } (*drives)[i] = NULL; return 0; } int partitionDrives(void) { struct deviceInfo drivesPresent[MAX_NUM_DRIVES]; int numDrivesPresent = 0; int childpid; int i, fd; int haveEdited = 0; char devBuf[100], idBuf[3]; char * driveText[50]; struct deviceInfo * currhd; int status; int reboot = 0; char * cmd; char * text; int rc, driveNum = 0; if (testing) cmd = "/sbin/fdisk"; else cmd = "/usr/bin/fdisk"; findDrivesPresent(drivesPresent, &numDrivesPresent); if (!numDrivesPresent) { newtWinMessage(_("Hard Drives"), _("Ok"), _("You don't have any hard drives available! " "You probably forgot to configure a SCSI " "controller.")); return INST_ERROR; } #ifdef __i386__ text = _("To install Red Hat Linux, you must have at least one parition " "of 50 MB dedicated to Linux. We suggest placing that partition " "on one of the first two hard drives in your system so you can " "boot into Linux with LILO."); #else text = _("To install Red Hat Linux, you must have at least one parition " "of 50 MB dedicated to Linux."); #endif for (i = 0; i < numDrivesPresent; i++) { sprintf(devBuf, "/dev/%s", drivesPresent[i].deviceName); if (!strncmp(drivesPresent[i].deviceName, "sd", 2)) { sprintf(idBuf, "%d", drivesPresent[i].id); strcat(devBuf, " - SCSI ID "); strcat(devBuf, idBuf); } strcat(devBuf, " - Model "); strcat(devBuf, drivesPresent[i].info); /* truncate at 50 columns for now */ devBuf[50] = '\0'; driveText[i] = alloca(strlen(devBuf)); strcpy(driveText[i], devBuf); } driveText[i] = NULL; do { rc = newtWinMenu(_("Partition Disks"), text, 56, 5, 6, 5, driveText, &driveNum, _("Done"), _("Edit"), _("Back"), NULL); if (rc == 2) { haveEdited = 1; currhd = drivesPresent + driveNum; sprintf(devBuf, "/tmp/%s", currhd->deviceName); if (devMakeInode(currhd->deviceName, devBuf)) return 0; newtSuspend(); for (i = 0; i < 25; i++) puts(""); printf("This is the fdisk program for partitioning your drive. It " "is running\non /dev/%s.\n\n", currhd->deviceName); logMessage("running fdisk on %s", devBuf); if (!(childpid = fork())) { execl(cmd, cmd, devBuf, NULL); return -1; } waitpid(childpid, &status, 0); newtResume(); } } while (rc == 2); if (haveEdited) { for (i = 0; i < numDrivesPresent; i++) { sprintf(devBuf, "/tmp/%s", drivesPresent[i].deviceName); if (devMakeInode(drivesPresent[i].deviceName, devBuf)) return INST_ERROR; fd = open(devBuf, O_RDONLY); unlink(devBuf); if (fd < 0) reboot = 1; if (ioctl(fd, BLKRRPART, 0)) reboot = 1; close(fd); } } if (reboot) { newtWinMessage("Reboot Needed", "Ok", "The kernel is unable to read your new partitioning " "information, probably because you modified extended " "partitions. While this is not critical, you must " "reboot your machine before proceeding. Insert the " "Red Hat boot disk now and press Return to reboot " "your system.\n\n" "If you have a ZIP or JAZ drive, make sure there is " "a disk in the drive as an empty SCSI drive can also " "cause this problem."); newtFinished(); exit(0); } if (rc == 3) return INST_CANCEL; return 0; }