network boot: support loading complete ISOs via HTTP

Now I don't have to unpack (or loop mount) ISO to boot via HTTP.
Also I can boot directly from any ALT mirror, like this:

automatic=method:http,network:dhcp,server:mirror.yandex.ru,directory=/altlinux/images/p9/simply/aarch64/slinux-live-9.1.1-aarch64.iso

Closes: #40710
This commit is contained in:
Alexey Sheplyakov 2021-08-08 14:03:54 +04:00
parent 736c89620f
commit 36b688d17a
5 changed files with 82 additions and 3 deletions

View File

@ -23,6 +23,7 @@
#define IMAGE_LOCATION "/image"
#define STAGE2_LOCATION "/root"
#define LIVE_DEVICE "/dev/loop0"
#define LOMOUNT_DEVICE "/dev/loop3"
#define STAGE2FS "squashfs"
#define LIVEFS "squashfs"
#ifndef STAGE2_BINNAME

View File

@ -133,7 +133,7 @@ set_loop (const char *device, const char *file)
}
char * loopdev = "/dev/loop3"; /* Ugly. But do I care? */
char * loopdev = LOMOUNT_DEVICE; /* Ugly. But do I care? */
void del_loop(char *device)
{

View File

@ -998,6 +998,7 @@ enum return_type http_prepare(void)
char *tmp;
int fd;
unsigned long size;
int is_iso = 0;
snprintf(location_full, sizeof(location_full),
"Please enter the name or IP address of the HTTP server, "
@ -1009,6 +1010,13 @@ enum return_type http_prepare(void)
}
strcpy(location_full, answers[1]);
log_message("HTTP: trying to retrieve %s", location_full);
fd = http_download_file(answers[0], location_full, &size);
if (fd >= 0) {
is_iso = 1;
goto download;
}
tmp = get_ramdisk_realname();
strcat(location_full, tmp);
free(tmp);
@ -1026,6 +1034,7 @@ enum return_type http_prepare(void)
continue;
}
download:
if (!ramdisk_possible(size)) {
close(fd);
stg1_error_message("HTTP install needs more than %u Mbytes of memory (detected %u Mbytes).",
@ -1035,7 +1044,7 @@ enum return_type http_prepare(void)
log_message("HTTP: size of download %lu bytes", size);
results = load_ramdisk_fd(fd, size);
results = load_ramdisk_or_iso(fd, size, is_iso);
close(fd);
if (results != RETURN_OK)
return RETURN_ERROR;

70
tools.c
View File

@ -45,6 +45,7 @@
#include "automatic.h"
#include <errno.h>
#include <linux/loop.h>
#include "lomount.h"
#include "tools.h"
#include "modules.h"
#include "sha256.h"
@ -297,6 +298,45 @@ static void save_stuff_for_rescue(void)
log_message("saved file %s for rescue (%d bytes)", file, i);
}
/**
* Loop mount ISO loaded to RAM, and open stage2
*
* @param ram_fd file descriptor of ISO image
* @return file descriptor of stage2 or -1 on error
*/
static int open_stage2_from_iso(int iso_fd)
{
int stage2_fd = -1;
const char *stage2 = get_ramdisk_path(IMAGE_LOCATION); /* /image/live */
if (!stage2) {
log_message("%s: get_ramdisk_size: got NULL", __func__);
goto out;
}
if (do_losetup_fd(LOMOUNT_DEVICE, iso_fd, NULL) < 0) {
log_message("%s: could not setup loopback for iso_fd", __func__);
goto out_free;
}
if (my_mount(LOMOUNT_DEVICE, IMAGE_LOCATION, "iso9660", 0)) {
log_message("%s: failed to mount ISO loopback", __func__);
goto out_del_loop;
}
stage2_fd = open(stage2, O_RDONLY);
if (stage2_fd < 0) {
log_message("%s: failed to open %s", __func__, stage2);
goto out_umount;
}
free((char *)stage2);
return stage2_fd;
out_umount:
umount(IMAGE_LOCATION);
out_del_loop:
del_loop(LOMOUNT_DEVICE);
out_free:
free((char *)stage2);
out:
return -1;
}
/**
* @brief copy exactly len bytes from buffer to file descriptor
@ -382,7 +422,7 @@ out:
return -1;
}
enum return_type load_ramdisk_fd(int source_fd, unsigned long size)
enum return_type load_ramdisk_or_iso(int source_fd, unsigned long size, int is_iso)
{
int ram_fd;
char * wait_msg = "Loading program into memory...";
@ -405,8 +445,26 @@ enum return_type load_ramdisk_fd(int source_fd, unsigned long size)
}
end_progression();
if (is_iso) {
int stage2_fd = open_stage2_from_iso(ram_fd);
if (stage2_fd < 0) {
close(ram_fd);
stg1_error_message("Could not open stage2 from ISO in RAM");
return RETURN_ERROR;
}
/* got a (mounted) loopback device backed by ram_fd,
* ram_fd itself is not necessary any more */
close(ram_fd);
/* to setup loopback device from stage2_fd */
ram_fd = stage2_fd;
}
if (do_losetup_fd(LIVE_DEVICE, ram_fd, NULL) < 0) {
close(ram_fd);
if (is_iso) {
umount(IMAGE_LOCATION);
del_loop(LOMOUNT_DEVICE);
}
stg1_error_message("Could not setup loopback for 2nd stage");
return RETURN_ERROR;
}
@ -416,6 +474,10 @@ enum return_type load_ramdisk_fd(int source_fd, unsigned long size)
if (my_mount(LIVE_DEVICE, STAGE2_LOCATION, STAGE2FS, 0)) {
stg1_error_message("Failed to mount stage2");
if (is_iso) {
umount(IMAGE_LOCATION);
del_loop(LOMOUNT_DEVICE);
}
return RETURN_ERROR;
}
@ -427,12 +489,18 @@ enum return_type load_ramdisk_fd(int source_fd, unsigned long size)
log_message("rescue: failed to umount " STAGE2_LOCATION);
return RETURN_ERROR;
}
if (is_iso) {
/* XXX: should I do anything special here? */
}
return RETURN_OK; /* fucksike, I lost several hours wondering why the kernel won't see the rescue if it is alreay mounted */
}
return RETURN_OK;
}
enum return_type load_ramdisk_fd(int source_fd, unsigned long size) {
return load_ramdisk_or_iso(source_fd, size, 0);
}
int splash_verbose()
{

View File

@ -36,6 +36,7 @@ int ramdisk_possible(unsigned long ramdisk_size);
char * get_ramdisk_realname(void);
enum return_type load_ramdisk(char *, unsigned long size);
enum return_type load_ramdisk_fd(int ramdisk_fd, unsigned long size);
enum return_type load_ramdisk_or_iso(int ramdisk_fd, unsigned long size, int is_iso);
void * memdup(void *src, size_t size);
void add_to_env(char * name, char * value);
void handle_env(char ** env);