propagator/disk.c
Michael Shigorin e285a0fe47 disk.c: optimize filesystem probing order
ISO9660 is the expected image partition fstype now,
and NTFS is really the last thing to be probed
as it needs extra modules to be present in stage1.
2014-04-14 21:54:02 +04:00

378 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"
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);
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 where is copied the %s Distribution.", 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;
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, "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, ntfs, iso9660, reiserfs).");
return RETURN_ERROR;
}
snprintf(buf, sizeof(buf), "Please enter the directory (or ISO image file) containing the %s Distribution.", version);
if (ask_from_entries_auto(buf, questions_location, &answers_location, 24, questions_location_auto, NULL) != RETURN_OK) {
umount(IMAGE_LOCATION);
return RETURN_ERROR;
}
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));
umount(IMAGE_LOCATION);
return RETURN_ERROR;
}
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);
umount(IMAGE_LOCATION);
return RETURN_ERROR;
}
iso = 1;
add_to_env("PIGGYBACK", "1");
}
if (access(get_ramdisk_path(iso ? NULL : location_full), 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));
loumount();
umount(IMAGE_LOCATION);
return RETURN_ERROR;
}
log_message("found the %s Installation, good news!", version);
if (!IS_LOWMEM && ramdisk_possible()) {
if (load_ramdisk(iso ? NULL : location_full) != RETURN_OK) {
stg1_error_message("Could not load program into memory.");
loumount();
umount(IMAGE_LOCATION);
return RETURN_ERROR;
}
} else {
do_losetup(LIVE_DEVICE, get_ramdisk_path(iso ? NULL : location_full));
my_mount(LIVE_DEVICE, STAGE2_LOCATION, (IS_LIVE) ? LIVEFS : STAGE2FS, 0);
}
add_to_env("DEVICE", choice);
add_to_env("METHOD", strdup("disk"));
add_to_env("PREFIX", answers_location[0]);
return RETURN_OK;
}
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 = 60;
update_splash("prepare");
fprintf(stderr,"%c[1A",27);
for(i=0; i<timeout; i++) {
sleep(1);
if(opendir("/dev/disk/"))break;
fprintf(stderr,"\rwaiting for /dev/disk/ %d...",i);
}
update_splash("wait_media");
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 (try_with_uuidlabel() == RETURN_OK)
return RETURN_OK;
if (count == 1) {
results = try_with_device(*medias);
return (results == RETURN_OK) ? RETURN_OK : RETURN_BACK;
}
snprintf(msg, sizeof(msg), "Please choose the DISK drive on which you copied the %s Distribution.", 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();
}