propagator/disk.c
2021-03-23 12:24:50 +07:00

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();
}