417 lines
11 KiB
C
417 lines
11 KiB
C
/*
|
|
* Guillaume Cottenceau (gc@mandrakesoft.com)
|
|
*
|
|
* Copyright 2000 MandrakeSoft
|
|
*
|
|
* This software may be freely redistributed under the terms of the GNU
|
|
* public license.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Portions from Erik Troan (ewt@redhat.com)
|
|
*
|
|
* Copyright 1996 Red Hat Software
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <glob.h>
|
|
#include <dirent.h>
|
|
#include "stage1.h"
|
|
#include "frontend.h"
|
|
#include "modules.h"
|
|
#include "probing.h"
|
|
#include "log.h"
|
|
#include "mount.h"
|
|
#include "lomount.h"
|
|
#include "automatic.h"
|
|
|
|
#include "disk.h"
|
|
#include "init.h"
|
|
#include "udev.h"
|
|
|
|
extern char version[];
|
|
|
|
struct partition_detection_anchor {
|
|
off_t offset;
|
|
const char * anchor;
|
|
};
|
|
|
|
static int seek_and_compare(int fd, struct partition_detection_anchor anch)
|
|
{
|
|
char buf[500];
|
|
int count;
|
|
if (lseek(fd, anch.offset, SEEK_SET) == (off_t)-1) {
|
|
log_perror("seek failed");
|
|
return -1;
|
|
}
|
|
count = read(fd, buf, strlen(anch.anchor));
|
|
if (count != strlen(anch.anchor)) {
|
|
log_perror("read failed");
|
|
return -1;
|
|
}
|
|
buf[count] = '\0';
|
|
if (strcmp(anch.anchor, buf))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static const char * detect_partition_type(char * dev)
|
|
{
|
|
struct partition_detection_info {
|
|
const char * name;
|
|
struct partition_detection_anchor anchor0;
|
|
struct partition_detection_anchor anchor1;
|
|
struct partition_detection_anchor anchor2;
|
|
} partitions_signatures[] = {
|
|
{ "Linux Swap", { 4086, "SWAP-SPACE" }, { 0, NULL }, { 0, NULL } },
|
|
{ "Linux Swap", { 4086, "SWAPSPACE2" }, { 0, NULL }, { 0, NULL } },
|
|
{ "Ext2", { 0x438, "\x53\xEF" }, { 0, NULL }, { 0, NULL } },
|
|
{ "ReiserFS", { 0x10034, "ReIsErFs" }, { 0, NULL }, { 0, NULL } },
|
|
{ "ReiserFS", { 0x10034, "ReIsEr2Fs" }, { 0, NULL }, { 0, NULL } },
|
|
{ "XFS", { 0, "XFSB" }, { 0x200, "XAGF" }, { 0x400, "XAGI" } },
|
|
{ "JFS", { 0x8000, "JFS1" }, { 0, NULL }, { 0, NULL } },
|
|
{ "NTFS", { 0x1FE, "\x55\xAA" }, { 0x3, "NTFS" }, { 0, NULL } },
|
|
{ "FAT32", { 0x1FE, "\x55\xAA" }, { 0x52, "FAT32" }, { 0, NULL } },
|
|
{ "FAT", { 0x1FE, "\x55\xAA" }, { 0x36, "FAT" }, { 0, NULL } },
|
|
{ "Linux LVM", { 0, "HM\1\0" }, { 0, NULL }, { 0, NULL } }
|
|
};
|
|
int partitions_signatures_nb = sizeof(partitions_signatures) / sizeof(struct partition_detection_info);
|
|
int i;
|
|
int fd;
|
|
|
|
char device_fullname[50];
|
|
strcpy(device_fullname, "/dev/");
|
|
strcat(device_fullname, dev);
|
|
|
|
if (ensure_dev_exists(device_fullname))
|
|
return NULL;
|
|
log_message("guessing type of %s", device_fullname);
|
|
|
|
if ((fd = open(device_fullname, O_RDONLY, 0)) < 0) {
|
|
log_perror("open");
|
|
return NULL;
|
|
}
|
|
|
|
for (i=0; i<partitions_signatures_nb; i++) {
|
|
int results = seek_and_compare(fd, partitions_signatures[i].anchor0);
|
|
if (results == -1)
|
|
goto detect_partition_type_end;
|
|
if (results == 1)
|
|
continue;
|
|
if (!partitions_signatures[i].anchor1.anchor)
|
|
goto detect_partition_found_it;
|
|
|
|
results = seek_and_compare(fd, partitions_signatures[i].anchor1);
|
|
if (results == -1)
|
|
goto detect_partition_type_end;
|
|
if (results == 1)
|
|
continue;
|
|
if (!partitions_signatures[i].anchor2.anchor)
|
|
goto detect_partition_found_it;
|
|
|
|
results = seek_and_compare(fd, partitions_signatures[i].anchor2);
|
|
if (results == -1)
|
|
goto detect_partition_type_end;
|
|
if (results == 1)
|
|
continue;
|
|
|
|
detect_partition_found_it:
|
|
return partitions_signatures[i].name;
|
|
}
|
|
|
|
detect_partition_type_end:
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
static char * disk_extract_list_directory(char * direct)
|
|
{
|
|
char ** full = list_directory(direct);
|
|
char tmp[2000] = "";
|
|
int i;
|
|
for (i=0; i<5 ; i++) {
|
|
if (!full || !*full)
|
|
break;
|
|
strcat(tmp, *full);
|
|
strcat(tmp, "\n");
|
|
full++;
|
|
}
|
|
return strdup(tmp);
|
|
}
|
|
|
|
static enum return_type try_with_partition(char *choice);
|
|
|
|
static enum return_type try_with_uuidlabel()
|
|
{
|
|
char device_fullname[128];
|
|
char device_pattern[128];
|
|
glob_t globbuf;
|
|
char devname[50];
|
|
struct stat statbuf;
|
|
int len;
|
|
|
|
strcpy(device_fullname, "/dev/disk/by-uuid/");
|
|
strcat(device_fullname, get_auto_value("uuid"));
|
|
if (stat(device_fullname, &statbuf) == 0 && S_ISBLK(statbuf.st_mode) &&
|
|
(len = readlink(device_fullname, devname, sizeof(devname))) != -1) {
|
|
devname[len] = 0;
|
|
return try_with_partition(strrchr(devname, '/') + 1);
|
|
}
|
|
|
|
strcpy(device_pattern, "/dev/disk/by-label/");
|
|
strcat(device_pattern, get_auto_value("label"));
|
|
/* Allow to use glob match in label, like automatic=method:disk,label:ALT* */
|
|
if( glob(device_pattern, 0, NULL, &globbuf) == 0 ) {
|
|
strcpy(device_fullname, globbuf.gl_pathv[0]);
|
|
if (stat(device_fullname, &statbuf) == 0 && S_ISBLK(statbuf.st_mode) &&
|
|
(len = readlink(device_fullname, devname, sizeof(devname))) != -1) {
|
|
devname[len] = 0;
|
|
globfree(&globbuf);
|
|
return try_with_partition(strrchr(devname, '/') + 1);
|
|
}
|
|
}
|
|
globfree(&globbuf);
|
|
return RETURN_ERROR;
|
|
}
|
|
|
|
static enum return_type try_with_device(char *dev_name)
|
|
{
|
|
int major, minor, blocks;
|
|
char name[100];
|
|
|
|
char buf[512];
|
|
FILE * f;
|
|
char * parts[50];
|
|
char * parts_comments[50];
|
|
int i = 0;
|
|
enum return_type results;
|
|
char * choice;
|
|
|
|
if (!(f = fopen("/proc/partitions", "rb")) || !fgets(buf, sizeof(buf), f) || !fgets(buf, sizeof(buf), f)) {
|
|
log_perror(dev_name);
|
|
if (f)
|
|
fclose(f);
|
|
stg1_error_message("Could not read partitions information.");
|
|
return RETURN_ERROR;
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
memset(name, 0, sizeof(name));
|
|
sscanf(buf, " %d %d %d %s", &major, &minor, &blocks, name);
|
|
if ((strstr(name, dev_name) == name) && (blocks > 1) && (name[strlen(dev_name)] != '\0')) {
|
|
const char * partition_type = detect_partition_type(name);
|
|
parts[i] = strdup(name);
|
|
parts_comments[i] = (char *) malloc(sizeof(char) * 100);
|
|
sprintf(parts_comments[i], "size: %d Mbytes", blocks >> 10);
|
|
if (partition_type) {
|
|
strcat(parts_comments[i], ", type: ");
|
|
strcat(parts_comments[i], partition_type);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
parts[i] = NULL;
|
|
fclose(f);
|
|
|
|
if (parts[0] == NULL) {
|
|
stg1_error_message("No partitions found.");
|
|
return RETURN_ERROR;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "Please choose the partition with %s distribution on boot media or cancel and try again.", version);
|
|
results = ask_from_list_comments_auto(buf, parts, parts_comments, &choice, "partition", parts);
|
|
if (results != RETURN_OK)
|
|
return results;
|
|
|
|
if (try_with_partition(choice) != RETURN_OK)
|
|
return try_with_device(dev_name);
|
|
else
|
|
return RETURN_OK;
|
|
}
|
|
|
|
static enum return_type try_with_partition(char *choice)
|
|
{
|
|
char * questions_location[] = { "Directory or ISO image", NULL };
|
|
char * questions_location_auto[] = { "directory", NULL };
|
|
static char ** answers_location = NULL;
|
|
|
|
char device_fullname[50];
|
|
char location_full[500];
|
|
char buf[512];
|
|
|
|
struct stat statbuf;
|
|
int iso = 0;
|
|
char *ramdisk_path = NULL;
|
|
unsigned long ramdisk_size;
|
|
|
|
strcpy(device_fullname, "/dev/");
|
|
strcat(device_fullname, choice);
|
|
|
|
if (my_mount(device_fullname, IMAGE_LOCATION, "iso9660", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "ext2", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "ext3", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "ext4", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "vfat", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "exfat", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "reiserfs", 0) == -1 &&
|
|
my_mount(device_fullname, IMAGE_LOCATION, "ntfs", 0) == -1) {
|
|
stg1_error_message("I can't find a valid filesystem (tried: ext2, ext3, ext4, vfat, exfat, ntfs, iso9660, reiserfs).");
|
|
return RETURN_ERROR;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "Please enter the directory (or ISO image file) containing %s distribution.", version);
|
|
if (ask_from_entries_auto(buf, questions_location, &answers_location, 24, questions_location_auto, NULL) != RETURN_OK) {
|
|
goto err;
|
|
}
|
|
|
|
strcpy(location_full, IMAGE_LOCATION);
|
|
strcat(location_full, "/");
|
|
strcat(location_full, answers_location[0]);
|
|
|
|
if (access(location_full, R_OK)) {
|
|
stg1_error_message("Directory or ISO image file could not be found on partition.\n"
|
|
"Here's a short extract of the files in the root of the partition:\n"
|
|
"%s", disk_extract_list_directory(IMAGE_LOCATION));
|
|
goto err;
|
|
}
|
|
|
|
if (!stat(location_full, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
|
|
log_message("%s exists and is not a directory, assuming this is an ISO image", location_full);
|
|
if (lomount(location_full, IMAGE_LOCATION)) {
|
|
stg1_error_message("Could not mount file %s as an ISO image of the %s Distribution.", answers_location[0], version);
|
|
goto err;
|
|
}
|
|
iso = 1;
|
|
add_to_env("PIGGYBACK", "1");
|
|
}
|
|
|
|
ramdisk_path = get_ramdisk_path(iso ? NULL : location_full);
|
|
if (ramdisk_path == NULL) {
|
|
stg1_error_message("Could not get ramdisk path");
|
|
goto err;
|
|
}
|
|
|
|
if (access(ramdisk_path, R_OK)) {
|
|
stg1_error_message("I can't find the %s Distribution in the specified directory. "
|
|
"Here's a short extract of the files in the directory:\n"
|
|
"%s", version, disk_extract_list_directory(IMAGE_LOCATION));
|
|
goto err;
|
|
}
|
|
|
|
ramdisk_size = get_ramdisk_size(ramdisk_path);
|
|
if (ramdisk_size == 0) {
|
|
stg1_error_message("Could not get %s size: %s", ramdisk_path, strerror(errno));
|
|
goto err;
|
|
}
|
|
|
|
log_message("found the %s Installation, good news!", version);
|
|
|
|
if (!IS_LOWMEM && ramdisk_possible(ramdisk_size)) {
|
|
if (load_ramdisk(ramdisk_path, ramdisk_size) != RETURN_OK) {
|
|
stg1_error_message("Could not load program into memory.");
|
|
goto err;
|
|
}
|
|
} else {
|
|
do_losetup(LIVE_DEVICE, ramdisk_path);
|
|
my_mount(LIVE_DEVICE, STAGE2_LOCATION, (IS_LIVE) ? LIVEFS : STAGE2FS, 0);
|
|
}
|
|
|
|
free(ramdisk_path);
|
|
|
|
add_to_env("DEVICE", choice);
|
|
add_to_env("METHOD", strdup("disk"));
|
|
add_to_env("PREFIX", answers_location[0]);
|
|
|
|
return RETURN_OK;
|
|
|
|
err:
|
|
free(ramdisk_path);
|
|
loumount();
|
|
umount(IMAGE_LOCATION);
|
|
return RETURN_ERROR;
|
|
}
|
|
|
|
enum return_type disk_prepare(void)
|
|
{
|
|
char ** medias, ** ptr, ** medias_models;
|
|
char * choice;
|
|
char msg[256];
|
|
int i, count = 0;
|
|
enum return_type results;
|
|
int timeout = 32;
|
|
DIR * diskdir;
|
|
|
|
update_splash("prepare");
|
|
fprintf(stderr,"%c[1A",27);
|
|
for(i=0; i<timeout; i++) {
|
|
if ((diskdir = opendir("/dev/disk/")) != NULL) {
|
|
closedir(diskdir);
|
|
break;
|
|
}
|
|
fprintf(stderr,"\rwaiting for /dev/disk/ %d...", i);
|
|
udev_settle();
|
|
sleep(1);
|
|
}
|
|
update_splash("wait_media");
|
|
|
|
if (IS_AUTOMATIC) {
|
|
unsigned int j;
|
|
|
|
/* Wait a bit for some slower/more sophisticated controllers to init */
|
|
for (j = 0; j < 15; ++j) {
|
|
udev_settle();
|
|
if (try_with_uuidlabel() == RETURN_OK)
|
|
return RETURN_OK;
|
|
|
|
wait_message("Waiting %d second%s for drive media to appear",
|
|
j, j > 1 ? "s" : "");
|
|
sleep(1);
|
|
remove_wait_message();
|
|
}
|
|
}
|
|
|
|
get_medias(DISK, &medias, &medias_models);
|
|
ptr = medias;
|
|
while (ptr && *ptr) {
|
|
count++;
|
|
ptr++;
|
|
}
|
|
update_splash("found_media");
|
|
if (count == 0) {
|
|
stg1_error_message("No DISK drive found.");
|
|
return RETURN_BACK;
|
|
}
|
|
|
|
if (count == 1) {
|
|
results = try_with_device(*medias);
|
|
return (results == RETURN_OK) ? RETURN_OK : RETURN_BACK;
|
|
}
|
|
|
|
snprintf(msg, sizeof(msg), "Please choose the boot disk drive with %s distribution or Cancel if not found yet.", version);
|
|
results = ask_from_list_comments_auto(msg, medias, medias_models, &choice, "disk", medias);
|
|
|
|
if (results != RETURN_OK)
|
|
return results;
|
|
|
|
results = try_with_device(choice);
|
|
if (results == RETURN_OK)
|
|
return RETURN_OK;
|
|
i = ask_insmod();
|
|
if (i == RETURN_BACK)
|
|
return RETURN_BACK;
|
|
return disk_prepare();
|
|
}
|