Initial s390-tools-2.0.0 import

This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# Common definitions
include ../../common.mak
ALL_CPPFLAGS += -I../include -I../boot \
-DZFCPDUMP_DIR=$(ZFCPDUMP_DIR) \
-DZFCPDUMP_FS_IMAGE=$(ZFCPDUMP_FS_IMAGE) \
-DZFCPDUMP_FS_RD=$(ZFCPDUMP_FS_RD) \
-DZFCPDUMP_PART_IMAGE=$(ZFCPDUMP_PART_IMAGE) \
-DZFCPDUMP_PART_RD=$(ZFCPDUMP_PART_RD) \
-D_FILE_OFFSET_BITS=64
ALL_LDFLAGS += -Wl,-z,noexecstack
libs = $(rootdir)/libutil/libutil.a \
$(rootdir)/libu2s/libu2s.a
objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
install.o zipl.o $(rootdir)/zipl/boot/data.o
zipl_helpers = $(wildcard zipl_helper.*)
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
all: zipl $(chreipl_helpers)
zipl: $(objects) $(libs)
chreipl_helper.%: zipl_helper.%
ln -s $< $@
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
$(DESTDIR)$(TOOLS_LIBDIR)
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
clean:
rm -f *.o $(chreipl_helpers) zipl
.PHONY: all install clean
# Additional manual dependencies
../boot/data.h:
make -C ../boot data.h
../boot/data.o:
make -C ../boot data.o
+565
View File
@@ -0,0 +1,565 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to handle the boot loader data
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../boot/data.h"
#include "boot.h"
#include "bootmap.h"
#include "error.h"
#include "misc.h"
#define DATA_SIZE(x) ((size_t) (&_binary_##x##_bin_size))
#define DATA_ADDR(x) (&_binary_##x##_bin_start)
#define STAGE2_MAX_SIZE 0x3000
#define STAGE1B_LOAD_ADDR 0xe000
#define CCW_FLAG_CC 0x40
#define CCW_FLAG_SLI 0x20
#define FBA_BLK_SIZE 512
static struct boot_ccw0 tic_to_stage1b = {
.cmd = 0x08, /* tic */
.address_lo = STAGE1B_LOAD_ADDR,
};
/* Check sizes of internal objects. Return 0 if everything is correct,
* non-zero otherwise. */
int
boot_check_data(void)
{
if (DATA_SIZE(fba0) != sizeof(struct boot_fba_stage0)) {
error_reason("Size mismatch of FBA stage 0 loader");
return -1;
}
if (DATA_SIZE(fba1b) != sizeof(struct boot_fba_stage1b)) {
error_reason("Size mismatch of FBA stage 1b loader");
return -1;
}
if (DATA_SIZE(eckd0_ldl) !=
sizeof(struct boot_eckd_ldl_stage0)) {
error_reason("Size mismatch of ECKD LDL stage 0 loader");
return -1;
}
if (DATA_SIZE(eckd0_cdl) != sizeof(struct boot_eckd_cdl_stage0)) {
error_reason("Size mismatch of ECKD CDL stage 0 loader");
return -1;
}
if (DATA_SIZE(eckd1) != sizeof(struct boot_eckd_stage1)) {
error_reason("Size mismatch of ECKD stage 1 loader");
return -1;
}
if (DATA_SIZE(eckd1b) != sizeof(struct boot_eckd_stage1b)) {
error_reason("Size mismatch of ECKD stage 1b loader");
return -1;
}
return 0;
}
/* Export stage 3 size for partition dump with dump kernel */
size_t
get_stage3_size()
{
return DATA_SIZE(stage3);
}
/* Create a stage 3 loader in memory.
* Upon success, return 0 and set BUFFER to point to the data buffer and set
* BYTECOUNT to contain the loader size in bytes. Return non-zero otherwise. */
int
boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
address_t initrd_addr, size_t initrd_len, address_t image_addr,
int extra_parm, uint16_t flags)
{
struct boot_stage3_params params;
void* data;
if (image_addr != (image_addr & PSW_ADDRESS_MASK)) {
error_reason("Kernel image load address to high (31 bit "
"addressing mode)");
return -1;
}
/* Get memory */
data = misc_malloc(DATA_SIZE(stage3));
if (data == NULL)
return -1;
/* Prepare params section */
params.parm_addr = (uint64_t) parm_addr;
params.initrd_addr = (uint64_t) initrd_addr;
params.initrd_len = (uint64_t) initrd_len;
params.load_psw = (uint64_t)(image_addr | PSW_LOAD);
params.extra_parm = (uint64_t) extra_parm;
params.flags = flags;
/* Initialize buffer */
memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3));
memcpy(data, &params, sizeof(struct boot_stage3_params));
*buffer = data;
*bytecount = DATA_SIZE(stage3);
return 0;
}
int
boot_init_fba_stage0(struct boot_fba_stage0 *stage0,
disk_blockptr_t *stage1b_list, blocknum_t stage1b_count)
{
blocknum_t i;
/* Initialize stage 0 data */
memcpy(stage0, DATA_ADDR(fba0), sizeof(*stage0));
/* Fill in blocklist for stage 2 loader */
if (stage1b_count > STAGE1B_BLK_CNT_MAX) {
error_reason("Not enough room for FBA stage 1b loader");
return -1;
}
for (i = 0; i < stage1b_count; i++) {
stage0->locdata[i].blocknr =
(uint32_t) stage1b_list[i].linear.block;
stage0->locread[i].read.address_lo =
STAGE1B_LOAD_ADDR + i * FBA_BLK_SIZE;
}
/* Terminate CCW chain: Tic to stage 1b */
memcpy(&stage0->locread[i], &tic_to_stage1b, sizeof(tic_to_stage1b));
return 0;
}
void
boot_init_eckd_ldl_stage0(struct boot_eckd_ldl_stage0 *stage0)
{
memcpy(stage0, DATA_ADDR(eckd0_ldl), sizeof(*stage0));
/* Fill in size of stage 1 plus stage 0 loader */
stage0->read_r1.count = sizeof(struct boot_eckd_stage1) +
sizeof(struct boot_eckd_ldl_stage0);
}
void
boot_init_eckd_cdl_stage0(struct boot_eckd_cdl_stage0 *stage0)
{
memcpy(stage0, DATA_ADDR(eckd0_cdl), sizeof(*stage0));
/* Fill in size of stage 1 loader */
stage0->read.count = sizeof(struct boot_eckd_stage1);
}
int
boot_init_eckd_stage1(struct boot_eckd_stage1 *stage1,
disk_blockptr_t *stage1b_list, blocknum_t stage1b_count)
{
blocknum_t i;
memcpy(stage1, DATA_ADDR(eckd1), sizeof(*stage1));
/* Fill in blocklist for stage 1b loader */
if (stage1b_count > STAGE1B_BLK_CNT_MAX) {
error_reason("Not enough room for ECKD stage 1b loader "
"(try larger block size)");
return -1;
}
for (i = 0; i < stage1b_count; i++) {
stage1->ssrt[i].read.count = stage1b_list[i].chs.size;
stage1->seek[i].cyl = stage1b_list[i].chs.cyl;
stage1->seek[i].head = stage1b_list[i].chs.head |
((stage1b_list[i].chs.cyl >> 12) & 0xfff0);
stage1->seek[i].sec = stage1b_list[i].chs.sec;
stage1->ssrt[i].read.address_lo =
STAGE1B_LOAD_ADDR + i * stage1b_list[i].chs.size;
stage1->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI;
}
/* Terminate CCW chain: Tic to stage 1b */
memcpy(&stage1->ssrt[i], &tic_to_stage1b, sizeof(tic_to_stage1b));
return 0;
}
int
boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b,
disk_blockptr_t *stage2_list, blocknum_t stage2_count)
{
blocknum_t i;
memcpy(stage1b, DATA_ADDR(fba1b), sizeof(*stage1b));
if (stage2_count > STAGE2_BLK_CNT_MAX) {
error_reason("Not enough room for FBA stage 2 loader");
return -1;
}
for (i = 0; i < stage2_count; i++) {
stage1b->locdata[i].blocknr =
(uint32_t) stage2_list[i].linear.block;
stage1b->locread[i].read.address_lo =
ZIPL_STAGE2_LOAD_ADDRESS + i * FBA_BLK_SIZE;
}
/* Terminate CCW chain */
stage1b->locread[i - 1].read.flags &= ~CCW_FLAG_CC;
return 0;
}
int
boot_init_eckd_stage1b(struct boot_eckd_stage1b *stage1b,
disk_blockptr_t *stage2_list, blocknum_t stage2_count)
{
blocknum_t i;
memcpy(stage1b, DATA_ADDR(eckd1b), sizeof(*stage1b));
if (stage2_count > STAGE2_BLK_CNT_MAX) {
error_reason("Not enough room for ECKD stage 2 loader "
"(try larger block size)");
return -1;
}
for (i = 0; i < stage2_count; i++) {
stage1b->ssrt[i].read.count = stage2_list[i].chs.size;
stage1b->seek[i].cyl = stage2_list[i].chs.cyl;
stage1b->seek[i].head = stage2_list[i].chs.head |
((stage2_list[i].chs.cyl >> 12) & 0xfff0);
stage1b->seek[i].sec = stage2_list[i].chs.sec;
stage1b->ssrt[i].read.address_lo = ZIPL_STAGE2_LOAD_ADDRESS +
i * stage2_list[i].chs.size;
stage1b->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI;
}
/* Terminate CCW chain */
stage1b->ssrt[i - 1].read.flags &= ~CCW_FLAG_CC;
return 0;
}
int
boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
address_t initrd_addr, address_t image_addr)
{
struct boot_tape_ipl_params params;
void* buffer;
if (image_addr != (image_addr & PSW_ADDRESS_MASK)) {
error_reason("Kernel image load address to high (31 bit "
"addressing mode)");
return -1;
}
buffer = misc_malloc(DATA_SIZE(tape0));
if (buffer == NULL)
return -1;
/* Prepare params section */
params.parm_addr = (uint64_t) parm_addr;
params.initrd_addr = (uint64_t) initrd_addr;
params.load_psw = (uint64_t) (image_addr | PSW_LOAD);
/* Initialize buffer */
memcpy(buffer, DATA_ADDR(tape0), DATA_SIZE(tape0));
memcpy(VOID_ADD(buffer, BOOT_TAPE_IPL_PARAMS_OFFSET), &params,
sizeof(struct boot_tape_ipl_params));
*data = buffer;
*size = DATA_SIZE(tape0);
return 0;
}
struct menu_buffer {
void *start;
size_t size;
size_t off;
};
static void
mb_init(struct menu_buffer *buffer, void *data, size_t size)
{
buffer-> start = data;
buffer->size = size;
buffer->off = 0;
memset(data, 0, size);
}
static void *
mb_alloc(struct menu_buffer *buffer, size_t len)
{
void *result;
if (buffer->off + len > buffer->size)
return NULL;
result = VOID_ADD(buffer->start, buffer->off);
buffer->off += len;
return result;
}
static void*
mb_sprintf(struct menu_buffer *buffer, const char *fmt, ...)
{
va_list args;
int size;
int len;
char *str;
str = VOID_ADD(buffer->start, buffer->off);
size = buffer->size - buffer->off;
va_start(args, fmt);
len = vsnprintf(str, size, fmt, args);
va_end(args);
if (len < 0 || len >= size)
return NULL;
misc_ascii_to_ebcdic((unsigned char *) str,
(unsigned char *) str + len);
mb_alloc(buffer, len + 1);
return str;
}
static int
store_stage2_menu(void* data, size_t size, struct job_data* job)
{
struct boot_stage2_params* params;
char* name;
int flag;
int timeout;
int i;
struct menu_buffer mb;
void *str;
uint64_t config_kdump = 0;
mb_init(&mb, data, size);
if (job->id == job_menu) {
name = "-";
for (i = 0; i < job->data.menu.num; i++) {
if (job->data.menu.entry[i].id == job_ipl &&
job->data.menu.entry[i].data.ipl.is_kdump)
/* we start with 2nd bit */
config_kdump |= (0x1 << (i + 1));
if (job->data.menu.entry[i].pos ==
job->data.menu.default_pos) {
if (job->data.menu.entry[i].id == job_ipl &&
job->data.menu.entry[i].data.ipl.is_kdump)
/* default entry is first bit */
config_kdump |= 0x1;
name = job->data.menu.entry[i].name;
}
}
flag = (job->data.menu.prompt != 0);
timeout = job->data.menu.timeout;
/* Be verbose */
if (verbose) {
printf("Preparing boot menu\n");
printf(" Interactive prompt......: %s\n",
job->data.menu.prompt ? "enabled" : "disabled");
if (job->data.menu.timeout == 0)
printf(" Menu timeout............: "
"disabled\n");
else
printf(" Menu timeout............: %d "
"seconds\n", job->data.menu.timeout);
printf(" Default configuration...: '%s'\n", name);
}
} else {
if (job->id == job_ipl && job->data.ipl.is_kdump)
config_kdump |= 0x1;
name = job->name;
flag = 0;
timeout = 0;
}
/* Header */
params = mb_alloc(&mb, sizeof(struct boot_stage2_params));
if (!params)
goto err_nospace;
params->flag = flag;
params->config_kdump = config_kdump;
params->timeout = timeout;
/* Banner text */
str = mb_sprintf(&mb, "zIPL v%s interactive boot menu\n ",
RELEASE_STRING);
if (!str)
goto err_nospace;
params->banner = (uint16_t) ((unsigned long) str -
(unsigned long) data);
/* Default config text */
if (name != NULL)
str = mb_sprintf(&mb, " 0. default (%s)", name);
else
str = mb_sprintf(&mb, " 0. default");
if (!str)
goto err_nospace;
params->config[0] = (uint16_t) ((unsigned long) str -
(unsigned long) data);
/* Skip rest if job is not an actual menu */
if (job->id != job_menu)
return 0;
/* Config texts */
for (i = 0; i < job->data.menu.num; i++) {
const char *kdump_str = "";
if (job->data.menu.entry[i].data.ipl.is_kdump)
kdump_str = " (kdump)";
str = mb_sprintf(&mb, "%2d. %s%s",
job->data.menu.entry[i].pos,
job->data.menu.entry[i].name,
kdump_str);
if (!str)
goto err_nospace_user;
params->config[job->data.menu.entry[i].pos] = (uint16_t)
((unsigned long) str - (unsigned long) data);
}
return 0;
err_nospace:
error_reason("Not enough room for menu data");
return -1;
err_nospace_user:
error_reason("Not enough room for menu data (try fewer sections or "
"shorter names)");
return -1;
}
int
boot_get_fba_stage2(void** data, size_t* size, struct job_data* job)
{
void* buffer;
int rc;
buffer = misc_malloc(STAGE2_MAX_SIZE);
if (buffer == NULL)
return -1;
memcpy(buffer, DATA_ADDR(fba2), DATA_SIZE(fba2));
rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(fba2)),
STAGE2_MAX_SIZE - DATA_SIZE(fba2),
job);
if (rc) {
free(buffer);
return rc;
}
*data = buffer;
*size = STAGE2_MAX_SIZE;
return 0;
}
int
boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job)
{
void* buffer;
int rc;
buffer = misc_malloc(STAGE2_MAX_SIZE);
if (buffer == NULL)
return -1;
memcpy(buffer, DATA_ADDR(eckd2), DATA_SIZE(eckd2));
rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(eckd2)),
STAGE2_MAX_SIZE - DATA_SIZE(eckd2),
job);
if (rc) {
free(buffer);
return rc;
}
*data = buffer;
*size = STAGE2_MAX_SIZE;
return 0;
}
int
boot_get_tape_dump(void** data, size_t* size, uint64_t mem)
{
void* buffer;
buffer = misc_malloc(DATA_SIZE(tape2dump));
if (buffer == NULL)
return -1;
memcpy(buffer, DATA_ADDR(tape2dump), DATA_SIZE(tape2dump));
/* Write mem size to end of dump record */
memcpy(VOID_ADD(buffer, DATA_SIZE(tape2dump) - sizeof(mem)), &mem,
sizeof(mem));
*data = buffer;
*size = DATA_SIZE(tape2dump);
return 0;
}
int
boot_get_eckd_dump_stage2(void** data, size_t* size, uint64_t mem)
{
void* buffer;
buffer = misc_malloc(DATA_SIZE(eckd2dump_sv));
if (buffer == NULL)
return -1;
memcpy(buffer, DATA_ADDR(eckd2dump_sv), DATA_SIZE(eckd2dump_sv));
/* Write mem size to end of dump record */
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_sv) - sizeof(mem)),
&mem, sizeof(mem));
*data = buffer;
*size = DATA_SIZE(eckd2dump_sv);
return 0;
}
int
boot_get_eckd_mvdump_stage2(void** data, size_t* size, uint64_t mem,
uint8_t force, struct mvdump_parm_table parm)
{
void* buffer;
buffer = misc_malloc(DATA_SIZE(eckd2dump_mv));
if (buffer == NULL)
return -1;
memcpy(buffer, DATA_ADDR(eckd2dump_mv), DATA_SIZE(eckd2dump_mv));
/* Write mem size and force indicator (as specified by zipl -M)
* to end of dump record, right before 512-byte parameter table */
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) - sizeof(mem) -
sizeof(struct mvdump_parm_table)), &mem, sizeof(mem));
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) - sizeof(mem) -
sizeof(force) - sizeof(struct mvdump_parm_table)),
&force, sizeof(force));
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) -
sizeof(struct mvdump_parm_table)), &parm,
sizeof(struct mvdump_parm_table));
*data = buffer;
*size = DATA_SIZE(eckd2dump_mv);
return 0;
}
int
boot_get_fba_dump_stage2(void** data, size_t* size, uint64_t mem)
{
void* buffer;
buffer = misc_malloc(DATA_SIZE(fba2dump));
if (buffer == NULL)
return -1;
memcpy(buffer, DATA_ADDR(fba2dump), DATA_SIZE(fba2dump));
/* Write mem size to end of dump record */
memcpy(VOID_ADD(buffer, DATA_SIZE(fba2dump) - sizeof(mem)),
&mem, sizeof(mem));
*data = buffer;
*size = DATA_SIZE(fba2dump);
return 0;
}
void
boot_get_dump_info(struct boot_info *boot_info, uint8_t dev_type, void *param)
{
memset(boot_info, 0, sizeof(*boot_info));
memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic));
boot_info->flags |= BOOT_INFO_FLAGS_ARCH;
boot_info->dev_type = dev_type;
boot_info->bp_type = BOOT_INFO_BP_TYPE_DUMP;
boot_info->version = BOOT_INFO_VERSION;
memcpy(&boot_info->bp.dump.param, param,
sizeof(boot_info->bp.dump.param));
}
void
boot_get_ipl_info(struct boot_info *boot_info, uint8_t dev_type,
disk_blockptr_t *bm_ptr, struct disk_info *info)
{
memset(boot_info, 0, sizeof(*boot_info));
memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic));
boot_info->flags |= BOOT_INFO_FLAGS_ARCH;
boot_info->dev_type = dev_type;
boot_info->bp_type = BOOT_INFO_BP_TYPE_IPL;
boot_info->version = BOOT_INFO_VERSION;
bootmap_store_blockptr(&boot_info->bp, bm_ptr, info);
}
+1046
View File
File diff suppressed because it is too large Load Diff
+1112
View File
File diff suppressed because it is too large Load Diff
+84
View File
@@ -0,0 +1,84 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to handle error messages
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdarg.h>
#include <stdio.h>
#include "error.h"
#define ERROR_STRING_SIZE 1024
static char error_reason_string[ERROR_STRING_SIZE];
static char error_text_string[ERROR_STRING_SIZE];
static int error_is_reason = 0;
static int error_is_text = 0;
/* Specify the actual reason why an operation failed by providing a formatted
* text string FMT and a variable amount of extra arguments. */
void
error_reason(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vsnprintf(error_reason_string, ERROR_STRING_SIZE, fmt, args);
va_end(args);
error_is_reason = 1;
}
/* Specify the (higher level) tool operation failure by providing a formatted
* text string FMT and a variable amount of extra arguments. */
void
error_text(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vsnprintf(error_text_string, ERROR_STRING_SIZE, fmt, args);
va_end(args);
error_is_text = 1;
}
/* Clear a previously specified error_reason() message. */
void
error_clear_reason(void)
{
error_is_reason = 0;
}
/* Clear a previously specified error_text() message. */
void
error_clear_text(void)
{
error_is_text = 0;
}
/* Print out the error reason and text message to stderr. */
void
error_print(void)
{
if (error_is_text && error_is_reason) {
fprintf(stderr, "Error: %s: %s\n", error_text_string,
error_reason_string);
} else if (error_is_text)
fprintf(stderr, "Error: %s\n", error_text_string);
else if (error_is_reason)
fprintf(stderr, "Error: %s\n", error_reason_string);
else
fprintf(stderr, "Error: An unspecified error occurred\n");
}
+1202
View File
File diff suppressed because it is too large Load Diff
+1847
View File
File diff suppressed because it is too large Load Diff
+606
View File
@@ -0,0 +1,606 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Miscellaneous helper functions
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "error.h"
#include "misc.h"
/* Allocate SIZE bytes of memory. Upon success, return pointer to memory.
* Return NULL otherwise. */
void *
misc_malloc(size_t size)
{
void* result;
result = malloc(size);
if (result == NULL) {
error_reason("Could not allocate %lld bytes of memory",
(unsigned long long) size);
}
return result;
}
/* asprintf with misc error checking */
int misc_asprintf(char** out, const char* fmt, ...)
{
va_list ap;
int rc;
va_start(ap, fmt);
rc = vasprintf(out, fmt, ap);
va_end(ap);
if (rc == -1)
error_reason("Could not allocate space for new string");
return rc;
}
/* Allocate N * SIZE bytes of memory. Upon success, return pointer to memory.
* Return NULL otherwise. */
void *
misc_calloc(size_t n, size_t size)
{
void* result;
result = calloc(n, size);
if (result == NULL) {
error_reason("Could not allocate %lld bytes of memory",
(unsigned long long) n *
(unsigned long long) size);
}
return result;
}
/* Duplicate the given string S. Upon success, return pointer to new string.
* Return NULL otherwise. */
char *
misc_strdup(const char* s)
{
char* result;
result = strdup(s);
if (result == NULL) {
error_reason("Could not allocate %lld bytes of memory",
(unsigned long long) strlen(s) + 1);
}
return result;
}
/* Open file exclusive */
int
misc_open_exclusive(const char* filename)
{
int fd;
fd = open(filename, O_RDWR | O_EXCL);
if (fd == -1 && errno == EBUSY)
error_reason("Device is in use (probably mounted)");
else if (fd == -1)
error_reason(strerror(errno));
return fd;
}
/* Read COUNT bytes of data from file identified by file descriptor FD to
* memory at location BUFFER. Return 0 when all bytes were successfully read,
* non-zero otherwise. */
int
misc_read(int fd, void* buffer, size_t count)
{
size_t done;
ssize_t rc;
for (done=0; done < count; done += rc) {
rc = read(fd, VOID_ADD(buffer, done), count - done);
if (rc == -1) {
error_reason(strerror(errno));
return -1;
}
if(rc == 0) {
error_reason("Reached unexpected end of file");
return -1;
}
}
return 0;
}
/* Read all of file FILENAME to memory. Upon success, return 0 and set BUFFER
* to point to the data and SIZE (if non-NULL) to contain the file size.
* If NIL_TERMINATE is non-zero, a nil-char will be added to the buffer string
* Return non-zero otherwise. */
int
misc_read_file(const char* filename, char** buffer, size_t* size,
int nil_terminate)
{
struct stat stats;
void* data;
int fd;
int rc;
if (stat(filename, &stats)) {
error_reason(strerror(errno));
return -1;
}
if (!S_ISREG(stats.st_mode)) {
error_reason("Not a regular file");
return -1;
}
data = misc_malloc(stats.st_size + (nil_terminate ? 1 : 0));
if (data == NULL)
return -1;
fd = open(filename, O_RDONLY);
if (fd == -1) {
error_reason(strerror(errno));
free(data);
return -1;
}
rc = misc_read(fd, data, stats.st_size);
close(fd);
if (rc) {
free(data);
return rc;
}
*buffer = data;
if (size != NULL)
*size = stats.st_size;
if (nil_terminate) {
if (size != NULL)
(*size)++;
(*buffer)[stats.st_size] = 0;
}
return 0;
}
#define INITIAL_FILE_BUFFER_SIZE 1024
/* Read file into buffer without querying its size (necessary for reading files
* from /proc or /sys). Upon success, return zero and set BUFFER to point to
* the file buffer and SIZE (if non-null) to contain the file size. Return
* non-zero otherwise. Add a null-byte at the end of the buffer if
* NIL_TERMINATE is non-zero. */
int
misc_read_special_file(const char* filename, char** buffer, size_t* size,
int nil_terminate)
{
FILE* file;
char* data;
char* new_data;
size_t count;
size_t current_size;
int current;
file = fopen(filename, "r");
if (file == NULL) {
error_reason(strerror(errno));
return -1;
}
current_size = INITIAL_FILE_BUFFER_SIZE;
count = 0;
data = (char *) misc_malloc(current_size);
if (data == NULL) {
fclose(file);
return -1;
}
current = fgetc(file);
while (current != EOF || nil_terminate) {
if (current == EOF) {
current = 0;
nil_terminate = 0;
}
data[count++] = (char) current;
if (count >= current_size) {
new_data = (char *) misc_malloc(current_size * 2);
if (new_data == NULL) {
free(data);
fclose(file);
return -1;
}
memcpy(new_data, data, current_size);
free(data);
data = new_data;
current_size *= 2;
}
current = fgetc(file);
}
fclose(file);
*buffer = data;
if (size)
*size = count;
return 0;
}
/* Get contents of file identified by FILENAME and fill in the respective
* fields of FILE. Return 0 on success, non-zero otherwise. */
int
misc_get_file_buffer(const char* filename, struct misc_file_buffer* file)
{
int rc;
rc = misc_read_file(filename, &file->buffer, &file->length, 0);
file->pos = 0;
return rc;
}
/* Free resources allocated for file buffer FILE. */
void
misc_free_file_buffer(struct misc_file_buffer* file)
{
if (file->buffer != NULL) {
free(file->buffer);
file->buffer = NULL;
file->pos = 0;
file->length = 0;
}
}
/* Return character at current FILE buffer position plus READAHEAD or EOF if
* at end of file. */
int
misc_get_char(struct misc_file_buffer* file, off_t readahead)
{
if (file->buffer != NULL)
if ((size_t) (file->pos + readahead) < file->length)
return file->buffer[file->pos + readahead];
return EOF;
}
char*
misc_make_path(char* dirname, char* filename)
{
char* result;
size_t len;
len = strlen(dirname) + strlen(filename) + 2;
result = (char *) misc_malloc(len);
if (result == NULL)
return NULL;
sprintf(result, "%s/%s", dirname, filename);
return result;
}
#define TEMP_DEV_MAX_RETRIES 1000
int
misc_temp_dev(dev_t dev, int blockdev, char** devno)
{
char* result;
char* pathname[] = { "/dev", getenv("TMPDIR"), "/tmp",
getenv("HOME"), "." , "/"};
char filename[] = "zipl0000";
mode_t mode;
unsigned int path;
int retry;
int rc;
int fd;
if (blockdev)
mode = S_IFBLK | S_IRWXU;
else
mode = S_IFCHR | S_IRWXU;
/* Try several locations as directory for the temporary device
* node. */
for (path=0; path < ARRAY_SIZE(pathname); path++) {
if (pathname[path] == NULL)
continue;
for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
sprintf(filename, "zipl%04d", retry);
result = misc_make_path(pathname[path], filename);
if (result == NULL)
return -1;
rc = mknod(result, mode, dev);
if (rc == 0) {
/* Need this test to cover 'nodev'-mounted
* filesystems. */
fd = open(result, O_RDWR);
if (fd != -1) {
close(fd);
*devno = result;
return 0;
}
remove(result);
retry = TEMP_DEV_MAX_RETRIES;
} else if (errno != EEXIST)
retry = TEMP_DEV_MAX_RETRIES;
free(result);
}
}
error_text("Unable to create temporary device node");
error_reason(strerror(errno));
return -1;
}
/* Create a temporary device node for the device containing FILE. Upon
* success, return zero and store a pointer to the name of the device node
* file into DEVNO. Return non-zero otherwise. */
int
misc_temp_dev_from_file(char* file, char** devno)
{
struct stat stats;
if (stat(file, &stats)) {
error_reason(strerror(errno));
return -1;
}
return misc_temp_dev(stats.st_dev, 1, devno);
}
/* Delete temporary device node DEVICE and free memory allocated for device
* name. */
void
misc_free_temp_dev(char* device)
{
if (remove(device)) {
fprintf(stderr, "Warning: Could not remove "
"temporary file %s: %s",
device, strerror(errno));
}
free(device);
}
/* Write COUNT bytes from memory at location DATA to the file identified by
* file descriptor FD. Return 0 when all bytes were successfully written,
* non-zero otherwise. */
int
misc_write(int fd, const void* data, size_t count)
{
size_t written;
ssize_t rc;
for (written=0; written < count; written += rc) {
rc = write(fd, VOID_ADD(data, written), count - written);
if (rc == -1) {
error_reason(strerror(errno));
error_text("Could not write to device");
return -1;
}
if (rc == 0) {
error_reason("Write failed");
error_text("Could not write to device");
return -1;
}
}
return 0;
}
int misc_seek(int fd, off_t off)
{
if (lseek(fd, off, SEEK_SET) == off)
return 0;
error_reason(strerror(errno));
error_text("Could not seek on device");
return -1;
}
int misc_pwrite(int fd, void *buf, size_t size, off_t off)
{
if (misc_seek(fd, off))
return -1;
return misc_write(fd, buf, size);
}
int
misc_check_writable_directory(const char* directory)
{
struct stat stats;
if (stat(directory, &stats)) {
error_reason(strerror(errno));
return -1;
}
if (!S_ISDIR(stats.st_mode)) {
error_reason("Not a directory");
return -1;
}
if (access(directory, W_OK)) {
error_reason(strerror(errno));
return -1;
}
return 0;
}
int
misc_check_readable_file(const char* filename)
{
struct stat stats;
if (stat(filename, &stats)) {
error_reason(strerror(errno));
return -1;
}
if (!S_ISREG(stats.st_mode)) {
error_reason("Not a regular file");
return -1;
}
if (access(filename, R_OK)) {
error_reason(strerror(errno));
return -1;
}
return 0;
}
int
misc_check_writable_device(const char* devno, int blockdev, int chardev)
{
struct stat stats;
if (stat(devno, &stats)) {
error_reason(strerror(errno));
return -1;
}
if (blockdev && chardev) {
if (!(S_ISCHR(stats.st_mode) || S_ISBLK(stats.st_mode))) {
error_reason("Not a device");
return -1;
}
} else if (blockdev) {
if (!S_ISBLK(stats.st_mode)) {
error_reason("Not a block device");
return -1;
}
} else if (chardev) {
if (!S_ISCHR(stats.st_mode)) {
error_reason("Not a character device");
return -1;
}
}
if (access(devno, W_OK)) {
error_reason(strerror(errno));
return -1;
}
return 0;
}
/* ASCII to EBCDIC conversion table. */
unsigned char ascebc[256] =
{
0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F,
0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D,
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF
};
/* EBCDIC to ASCII conversion table. */
unsigned char ebcasc[256] =
{
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
/* 0x40 SP RSP ? ---- */
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
/* 0x48 . < ( + | */
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
/* 0x50 & ---- */
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
/* 0x58 ? ! $ * ) ; */
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
/* 0x60 - / ---- ? ---- ---- ---- */
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
/* 0x68 ---- , % _ > ? */
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x78 * ` : # @ ' = " */
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
/* 0x80 * a b c d e f g */
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
/* 0x88 h i ---- ---- ---- */
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
/* 0x90 ? j k l m n o p */
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
/* 0x98 q r ---- ---- */
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
/* 0xA0 ~ s t u v w x */
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
/* 0xA8 y z ---- ---- ---- ---- */
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
/* 0xB0 ^ ---- ? ---- */
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
/* 0xC0 { A B C D E F G */
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
/* 0xC8 H I ---- ? ---- */
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
/* 0xD0 } J K L M N O P */
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
/* 0xD8 Q R ---- ? */
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
/* 0xE0 \ S T U V W X */
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
/* 0xE8 Y Z ---- ? ---- ---- ---- */
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
/* 0xF0 0 1 2 3 4 5 6 7 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
/* 0xF8 8 9 ---- ---- ? ---- ---- ---- */
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
};
void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to)
{
for (; from != to; from++)
*from = ebcasc[*from];
}
void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to)
{
for (; from != to; from++)
*from = ascebc[*from];
}
+1819
View File
File diff suppressed because it is too large Load Diff
+242
View File
@@ -0,0 +1,242 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Main function
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "boot.h"
#include "bootmap.h"
#include "disk.h"
#include "error.h"
#include "install.h"
#include "job.h"
#include "misc.h"
#include "zipl.h"
/* Flag deciding the level of verbosity */
int verbose = 0;
/* Flag deciding whether confirmation questions are asked */
int interactive = 1;
/* Flag deciding whether actions should only be simulated */
int dry_run = 1;
/* Full tool name */
static const char tool_name[] = "zipl: zSeries Initial Program Loader";
/* Copyright notice */
static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2017";
/* Usage information */
static const char* usage_text[] = {
"Usage: zipl [OPTIONS] [SECTION]",
"",
"Prepare a device for initial program load. Use OPTIONS described below or ",
"provide the name of a SECTION defined in the zIPL configuration file.",
"",
"-h, --help Print this help, then exit",
"-v, --version Print version information, then exit",
"-c, --config CONFIGFILE Read configuration from CONFIGFILE",
"-t, --target TARGETDIR Write bootmap file to TARGETDIR and install",
" bootloader on device containing TARGETDIR",
" --targetbase BASEDEVICE Install bootloader on BASEDEVICE",
" --targettype TYPE Use device type: CDL, LDL, FBA, SCSI",
" --targetgeometry C,H,S Use disk geometry: cylinders,heads,sectors",
" --targetblocksize SIZE Use number of bytes per block",
" --targetoffset OFFSET Use offset between logical and physical disk",
"-i, --image IMAGEFILE[,ADDR] Install Linux kernel image from IMAGEFILE",
"-r, --ramdisk RAMDISK[,ADDR] Install initial ramdisk from file RAMDISK",
"-p, --parmfile PARMFILE[,ADDR] Use kernel parmline stored in PARMFILE",
"-P, --parameters PARMLINE Use specified kernel PARMLINE",
"-T, --tape TAPEDEV Install bootloader on tape device TAPEDEV",
"-s, --segment SEGMENT,ADDR Install a segment from file SEGMENT",
"-d, --dumpto DUMPDEV[,SIZE] Install a system dump record on tape device",
" or disk partition DUMPDEV",
"-M, --mvdump DEVLIST[,SIZE] Install a multi-volume dump record on each",
" disk partition listed in file DEVLIST",
"-f, --force Disable sanity check while producing a",
" multi-volume dump",
"-m, --menu MENU Install multi-boot configuration MENU",
"-n, --noninteractive Answer all confirmation questions with 'yes'",
"-V, --verbose Provide more verbose output",
"-a, --add-files Add all referenced files to bootmap file",
" --dry-run Simulate run but don't modify IPL records"
};
/* Print usage information. */
static void
print_usage(void)
{
unsigned int i;
for (i=0; i < ARRAY_SIZE(usage_text); i++)
printf("%s\n", usage_text[i]);
}
/* Print version information. */
static void
print_version(void)
{
printf("%s version %s\n", tool_name, RELEASE_STRING);
printf("%s\n", copyright_notice);
}
/* Check whether calling user is root. Return 0 if user is root, non-zero
* otherwise. */
static int
check_for_root(void)
{
if (geteuid() != 0) {
error_clear_text();
error_reason("Must be root to perform this operation");
return -1;
} else
return 0;
}
int
main(int argc, char* argv[])
{
struct disk_info* info;
disk_blockptr_t program_table, scsi_dump_sb_blockptr, *stage1b_list;
blocknum_t stage1b_count;
struct job_data* job;
char* device;
int rc;
/* Check internals */
rc = boot_check_data();
if (rc) {
error_text("Internal error");
error_print();
return 1;
}
/* Find out what we're supposed to do */
rc = job_get(argc, argv, &job);
if (rc) {
error_print();
return 1;
}
/* Check for priority options --help and --version */
if (job->id == job_print_usage) {
print_usage();
job_free(job);
return 0;
} else if (job->id == job_print_version) {
print_version();
job_free(job);
return 0;
}
/* Make sure we're running as root */
if (check_for_root()) {
job_free(job);
error_print();
return 1;
}
/* Set global option variables */
verbose = job->verbose;
interactive = !job->noninteractive;
dry_run = job->dry_run;
if (dry_run)
printf("Starting dry-run, target device contents will NOT be "
"modified\n");
/* Make sure new files are only user-accessible */
umask(077);
/* Do it */
switch (job->id) {
case job_dump_partition:
if (disk_is_tape(job->data.dump.device) ||
!disk_is_scsi(job->data.dump.device, &job->target)) {
rc = install_dump(job->data.dump.device, &job->target,
job->data.dump.mem);
break;
}
/* Dump to a raw SCSI partition */
if (job->data.dump.mem != -1uLL) {
error_reason("Dump size can not be limited for "
"partition dump on a SCSI disk");
rc = -1;
break;
}
rc = check_job_dump_images(&job->data.dump, job->name);
if (rc != 0)
break;
/* Fall through. */
case job_ipl:
case job_segment:
case job_menu:
/* Create bootmap */
stage1b_list = NULL;
rc = bootmap_create(job, &program_table, &scsi_dump_sb_blockptr,
&stage1b_list, &stage1b_count, &device,
&info);
if (rc)
break;
/* Install boot loader */
rc = install_bootloader(device, &program_table,
&scsi_dump_sb_blockptr, stage1b_list,
stage1b_count, info, job);
if (stage1b_list != NULL)
free(stage1b_list);
misc_free_temp_dev(device);
disk_free_info(info);
break;
case job_ipl_tape:
rc = install_tapeloader(job->data.ipl_tape.device,
job->data.ipl_tape.image,
job->data.ipl_tape.parmline,
job->data.ipl_tape.ramdisk,
job->data.ipl_tape.image_addr,
job->data.ipl_tape.parm_addr,
job->data.ipl_tape.ramdisk_addr);
break;
case job_mvdump:
rc = install_mvdump(job->data.mvdump.device,
&job->target,
job->data.mvdump.device_count,
job->data.mvdump.mem,
job->data.mvdump.force);
break;
case job_print_usage:
case job_print_version:
/* Should not happen */
break;
}
switch (rc) {
case 0: /* Operation completed successfully */
if (verbose)
printf("Syncing disks...\n");
if (!dry_run)
sync();
printf("Done.\n");
break;
case -2: /* Operation canceled by user */
break;
default: /* An error occurred */
error_print();
break;
}
job_free(job);
return abs(rc);
}
+862
View File
@@ -0,0 +1,862 @@
#!/usr/bin/perl -w
#
# zipl_helper.device-mapper: print zipl parameters for a device-mapper device
#
# Copyright IBM Corp. 2009, 2017
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
#
# Depending on the name by which the script is called, it serves one of two
# purposes:
#
# 1. Usage: zipl_helper.device-mapper <target directory> or
# <major:minor of target device>
#
# This tool attempts to obtain zipl parameters for a target directory or
# partition located on a device-mapper device. It assumes that the
# device-mapper table for this device conforms to the following rules:
# - directory is located on a device consisting of a single device-mapper
# target
# - only linear, mirror and multipath targets are supported
# - supported physical device types are DASD and SCSI devices
# - all of the device which contains the directory must be located on a single
# physical device (which may be mirrored or accessed through a multipath
# target)
# - any mirror in the device-mapper setup must include block 0 of the
# physical device
#
# 2. Usage: chreipl_helper.device-mapper <major:minor of target device>
#
# This tool identifies the physical device which contains the specified
# device-mapper target devices. If the physical device was found, its
# major:minor parameters are printed. Otherwise, the script exits with an
# error message and a non-zero return code.
#
use strict;
use File::Basename;
use POSIX qw/locale_h/;
# Required tools
our $dmsetup = "dmsetup";
our $mknod = "mknod";
our $dasdview = "dasdview";
our $blockdev = "blockdev";
# Constants
our $SECTOR_SIZE = 512;
our $DASD_PARTN_MASK = 0x03;
our $SCSI_PARTN_MASK = 0x0f;
# Internal constants
our $DEV_TYPE_CDL = 0;
our $DEV_TYPE_LDL = 1;
our $DEV_TYPE_FBA = 2;
our $DEV_TYPE_SCSI = 3;
our $TARGET_START = 0;
our $TARGET_LENGTH = 1;
our $TARGET_TYPE = 2;
our $TARGET_DATA = 3;
our $TARGET_TYPE_LINEAR = 0;
our $TARGET_TYPE_MIRROR = 1;
our $TARGET_TYPE_MULTIPATH = 2;
our $LINEAR_MAJOR = 0;
our $LINEAR_MINOR = 1;
our $LINEAR_START_SECTOR = 2;
our $MIRROR_MAJOR = 0;
our $MIRROR_MINOR = 1;
our $MIRROR_START_SECTOR = 2;
our $MULTIPATH_MAJOR = 0;
our $MULTIPATH_MINOR = 1;
sub get_physical_device_dir($);
sub get_physical_device($$);
sub get_major_minor($);
sub get_table($$);
sub get_linear_data($$);
sub get_mirror_data($$);
sub get_multipath_status($);
sub get_multipath_data($$);
sub filter_table($$$);
sub get_target_start($);
sub get_target_major_minor($);
sub create_temp_device_node($$$);
sub get_blocksize($);
sub get_dasd_info($);
sub get_partition_start($$);
sub is_dasd($);
sub get_partition_base($$$);
sub get_device_characteristics($$);
sub get_type_name($);
sub check_for_mirror($@);
sub get_target_base($$$$@);
sub get_device_name($$);
sub check_tools();
my $phy_geometry; # Disk geometry of physical device
my $phy_blocksize; # Blocksize of physical device
my $phy_offset; # Offset in 512-byte sectors between start of physical
# device and start of filesystem
my $phy_type; # Type of physical device
my $phy_bootsectors; # Size of boot record in 512-byte sectors
my $phy_partstart; # Partition offset of physical device
my $phy_major; # Major device number of physical device
my $phy_minor; # Minor device number of physical device
my @target_list; # List of dm-targets between filesystem and physical
# device.
my $base_major; # Major device number of base device.
my $base_minor; # Minor device number of base device
my $directory; # Command line parameter
my $toolname; # Name of tool
# Start
$toolname = basename($0);
# Setup and use a standard locale to
# avoid localized scripting output
$ENV{LC_ALL} = "C"; # for child processes
setlocale(LC_ALL, "C"); # for current process
# Use alternate code path if called as chreipl helper
if ($toolname eq "chreipl_helper.device-mapper") {
if (!defined($ARGV[0]) || !($ARGV[0] =~ /^\s*(\d+)\s*:\s*(\d+)\s*$/)) {
die("Usage: $toolname <major:minor of target devies>\n");
}
($phy_major, $phy_minor, $phy_offset, @target_list) =
get_physical_device($1, $2);
print("$phy_major:$phy_minor\n");
exit(0);
}
$directory = $ARGV[0];
if (!defined($directory)) {
die("Usage: $toolname <target directory> or <major:minor of target devies>\n");
}
# check if needed tools are available
check_tools();
if (($ARGV[0] =~ /^\s*(\d+)\s*:\s*(\d+)\s*$/)) {
# Determine physical (non-dm) device on which partition is located
($phy_major, $phy_minor, $phy_offset, @target_list) =
get_physical_device($1,$2);
}
else
{
# Determine physical (non-dm) device on which directory is located
($phy_major, $phy_minor, $phy_offset, @target_list) =
get_physical_device_dir($directory);
}
# Determine type and characteristics of physical device
($phy_type, $phy_blocksize, $phy_geometry, $phy_bootsectors, $phy_partstart) =
get_device_characteristics($phy_major, $phy_minor);
# Handle partitions
if ($phy_partstart > 0) {
# Only the partition of the physical device is mapped so only the
# physical device can provide access to the boot record.
($base_major, $base_minor) =
get_partition_base($phy_type, $phy_major, $phy_minor);
# Check for mirror
check_for_mirror(scalar(@target_list) - 1, @target_list);
# Adjust filesystem offset
$phy_offset += $phy_partstart * ($phy_blocksize / $SECTOR_SIZE);
$phy_partstart = 0;
# Update device geometry
(undef, undef, $phy_geometry, undef, undef) =
get_device_characteristics($base_major, $base_minor);
} else {
# All of the device is mapped, so the base device is the top most
# dm device which provides access to boot sectors
($base_major, $base_minor) =
get_target_base($phy_major, $phy_minor, 0, $phy_bootsectors,
@target_list);
}
# Check for valid offset of file system
if (($phy_offset % ($phy_blocksize / $SECTOR_SIZE)) != 0) {
die("Error: File system not aligned on physical block size\n");
}
# Print resulting information
print("targetbase=$base_major:$base_minor\n");
print("targettype=".get_type_name($phy_type)."\n");
if (defined($phy_geometry)) {
print("targetgeometry=$phy_geometry\n");
}
print("targetblocksize=$phy_blocksize\n");
print("targetoffset=".($phy_offset / ($phy_blocksize / $SECTOR_SIZE))."\n");
exit(0);
# get_physical_device_from_dir(dir)
# Returns (phy_major, phy_minor, phy_offset, @target_list).
# target_list: [target_data1, target_data2, ..., target_datan]
# target_data: [major, minor, target]
sub get_physical_device_dir($)
{
my ($directory) = @_;
my ($major, $minor) = get_major_minor($directory);
return get_physical_device($major, $minor);
}
# get_physical_device(major, minor)
# Returns (phy_major, phy_minor, phy_offset, @target_list).
# target_list: [target_data1, target_data2, ..., target_datan]
# target_data: [major, minor, target]
sub get_physical_device($$)
{
my ($major, $minor) = @_;
my $table;
my $target;
my $start;
my $length;
my @target_list;
$table = get_table($major, $minor);
if (scalar(@$table) == 0) {
die("Error: Could not retrieve device-mapper information for ".
"device '".get_device_name($major, $minor)."'\n");
}
# Filesystem must be on a single dm target
if (scalar(@$table) != 1) {
die("Error: Unsupported setup: Directory '$directory' is ".
"located on a multi-target device-mapper device\n");
}
$target = $table->[0];
push(@target_list, [$major, $minor, $target]);
$start = $target->[$TARGET_START];
$length = $target->[$TARGET_LENGTH];
while (1) {
# Convert fs_start to offset on parent dm device
$start += get_target_start($target);
($major, $minor) = get_target_major_minor($target);
$table = get_table($major, $minor);
if (scalar(@$table) == 0) {
# Found non-dm device
return ($major, $minor, $start, @target_list);
}
# Get target in parent table which contains filesystem
$table = filter_table($table, $start, $length);
if (scalar(@$table) != 1) {
die("Error: Unsupported setup: Could not map ".
"directory '$directory' to a single physical ".
"device\n");
}
$target = $table->[0];
push(@target_list, [$major, $minor, $target]);
# Convert fs_start to offset on parent target
$start -= $target->[$TARGET_START];
}
}
# get_major_minor(filename)
# Returns: (device major, device minor) of the device containing the
# specified file.
sub get_major_minor($)
{
my ($filename) = @_;
my @stat;
my $dev;
my $major;
my $minor;
@stat = stat($filename);
if (!@stat) {
die("Error: Could not stat '$filename'\n");
}
$dev = $stat[0];
$major = ($dev & 0xfff00) >> 8;
$minor = ($dev & 0xff) | (($dev >> 12) & 0xfff00);
return ($major, $minor);
}
# get_table(major, minor)
# Returns: [target1, target2, ..., targetn]
# target: [start, length, type, data]
# data: linear_data|mirror_data|multipath_data
sub get_table($$)
{
my ($major, $minor) = @_;
my @table;
my $dev_name = get_device_name($major, $minor);
local *HANDLE;
open(HANDLE, "$dmsetup table -j $major -m $minor 2>/dev/null|") or
return undef;
while (<HANDLE>) {
if (!(/^(\d+)\s+(\d+)\s+(\S+)\s+(\S.*)$/)) {
die("Error: Unrecognized device-mapper table format ".
"for device '$dev_name'\n");
}
my ($start, $length, $target_type, $args) = ($1, $2, $3, $4);
my $data;
my $type;
if ($target_type eq "linear") {
$type = $TARGET_TYPE_LINEAR;
$data = get_linear_data($dev_name, $args);
} elsif ($target_type eq "mirror") {
$type = $TARGET_TYPE_MIRROR;
$data = get_mirror_data($dev_name, $args);
} elsif ($target_type eq "multipath") {
$type = $TARGET_TYPE_MULTIPATH;
$data = get_multipath_data($dev_name, $args);
} else {
die("Error: Unsupported setup: Unsupported ".
"device-mapper target type '$target_type' for ".
"device '$dev_name'\n");
}
push(@table, [$start, $length, $type, $data]);
}
close(HANDLE);
return \@table;
}
# get_linear_data(dev_name, args)
# Returns: [major, minor, start_sector]
sub get_linear_data($$)
{
my ($dev_name, $args) = @_;
if (!($args =~ /^(\d+):(\d+)\s+(\d+)$/)) {
die("Error: Unrecognized device-mapper table format for ".
"device '$dev_name'\n");
}
return [$1, $2, $3];
}
# get_mirror_data(dev_name, args)
# Returns [[major1, minor1, start_sector1], [major2, minor2, start_sector2], ..]
sub get_mirror_data($$)
{
my ($dev_name, $args) = @_;
my @argv = split(/\s+/, $args);
my @data;
my $offset;
my $num;
# Remove log_type + #logargs + logargs
splice(@argv, 0, $argv[1] + 2);
if (!@argv) {
goto out_error;
}
$num = shift(@argv);
while ($num-- > 0) {
if (!($argv[0] =~ /^(\d+):(\d+)$/)) {
goto out_error;
}
push(@data, [$1, $2, $argv[1]]);
if (!defined($offset)) {
$offset = $argv[1];
} elsif ($argv[1] != $offset) {
die("Error: Unsupported setup: Mirror target on ".
"device '$dev_name' contains entries with varying ".
"sector offsets\n");
}
splice(@argv, 0, 2);
}
if (!scalar(@data)) {
goto out_error;
}
return \@data;
out_error:
die("Error: Unrecognized device-mapper table format for device ".
"'$dev_name'\n");
}
# get_multipath_status(device)
# Return map of nodes to F/A flags (i.e. $map{$node} is either "A" or "F").
# See linux/drivers/md/dm-mpath.c:multipath_status for details.
sub get_multipath_status($)
{
my $dev = shift();
my %map;
my $str;
my $failed = 0;
open(my $fh, "$dmsetup status /dev/$dev 2>/dev/null|") or return undef;
while ($str = <$fh>) {
# sample output (single line):
# 0 67108864 multipath \
# 2 0 0 \
# 0 \
# 2 2 \
# E 0 \
# 2 2 \
# 8:16 F 1 \
# 0 1 \
# 8:0 F 1 \
# 0 1 \
# A 0 \
# 2 2 \
# 8:32 A 0 \
# 0 1 \
# 8:48 A 0 \
# 0 1
my @line = split(/\s+/, $str);
next if !@line;
my ($start, $length, $type) = splice(@line, 0, 3);
next if $type ne "multipath";
my $cnt = shift(@line); # Remove #multipath_feature_args +
splice(@line, 0, $cnt) if $cnt > 0; # multipath_feature_args
$cnt = shift(@line); # Remove #handler_status_args +
splice(@line, 0, $cnt) if $cnt > 0; # handler_status_args
# num_groups init_group_number ...
my ($ngr, $ign) = splice(@line, 0, 2);
for (my $g = 0; $g < $ngr; $g++) {
# Remove group_state + #ps_status_args
# group_state: D(isabled), A(ctive), or E(nabled)
my ($state, $cnt) = splice(@line, 0, 2);
# Remove ps_status_args*
splice(@line, 0, $cnt) if $cnt > 0;
# Remove #paths + #selector_args
my ($paths, $nsa) = splice(@line, 0, 2);
for (my $p = 0; $p < $paths; $p++) {
# Fetch single path description
my ($node, $active, $fail_cnt)
= splice(@line, 0, 3);
# active: A(ctive) or F(ailed)
$map{$node} = $active;
$failed++ if $active ne "A";
# Remove selector_args*
splice(@line, 0, $nsa) if $nsa > 0;
}
}
}
close($fh);
die ("Error: No paths found for '$dev'\n") if scalar(keys %map) == 0;
if ($failed) {
die ("Error: All paths for '$dev' failed\n")
if $failed == scalar(keys %map);
print(STDERR "Warning: There are one or more failed paths for"
." device '$dev'\n");
}
return \%map;
}
# get_multipath_data(dev_name, args)
# Returns [[major1, minor1], [major2, minor2], ..]
sub get_multipath_data($$)
{
my ($dev_name, $args) = @_;
my $status = get_multipath_status($dev_name);
my @argv = split(/\s+/, $args);
my @data;
# Remove #features + features
splice(@argv, 0, $argv[0] + 1);
if (!@argv) {
goto out_error;
}
# Remove #handlerargs + handlerargs
splice(@argv, 0, $argv[0] + 1);
if (!@argv) {
goto out_error;
}
# Remove #pathgroups + pathgroup
splice(@argv, 0, 2);
while (@argv) {
# Remove pathselector + #selectorargs + selectorargs
splice(@argv, 0, 2 + $argv[1]);
if (!@argv) {
goto out_error;
}
my $num_paths = $argv[0];
my $num_path_args = $argv[1];
# Remove #paths + #pathargs
splice(@argv, 0, 2);
while ($num_paths-- > 0) {
if (!@argv) {
goto out_error;
}
if (!($argv[0] =~ /(\d+):(\d+)/)) {
goto out_error;
}
push(@data, [$1, $2]) if $status->{$argv[0]} eq "A";
# Remove device + deviceargs
splice(@argv, 0, 1 + $num_path_args);
}
}
if (!@data) {
goto out_error;
}
return \@data;
out_error:
die("Error: Unrecognized device-mapper table format for device ".
"'$dev_name'\n");
}
# filter_table(table, start, length)
# Returns table containing only targets between start and start + length - 1.
sub filter_table($$$)
{
my ($table, $start, $length) = @_;
my $end = $start + $length - 1;
my @result;
my $target;
foreach $target (@$table) {
my $target_start = $target->[$TARGET_START];
my $target_end = $target_start + $target->[$TARGET_LENGTH] - 1;
if (!(($target_end < $start) || ($target_start > $end))) {
push(@result, $target);
}
}
return \@result;
}
# get_target_start(target)
# Returns the start sector of target.
sub get_target_start($)
{
my ($target) = @_;
my $type = $target->[$TARGET_TYPE];
my $data = $target->[$TARGET_DATA];
if ($type == $TARGET_TYPE_LINEAR) {
return $data->[$LINEAR_START_SECTOR];
} elsif ($type == $TARGET_TYPE_MIRROR) {
my $mirror_data = $data->[0];
return $mirror_data->[$MIRROR_START_SECTOR];
} else {
return 0;
}
}
# get_target_major_minor(target)
# Returns (major, minor) of target of target.
sub get_target_major_minor($)
{
my ($target) = @_;
my $type = $target->[$TARGET_TYPE];
my $data = $target->[$TARGET_DATA];
my $major;
my $minor;
if ($type == $TARGET_TYPE_LINEAR) {
$major = $data->[$LINEAR_MAJOR];
$minor = $data->[$LINEAR_MINOR];
} elsif ($type == $TARGET_TYPE_MIRROR) {
# Use data of first device in list
my $mirror_data = $data->[0];
$major = $mirror_data->[$MIRROR_MAJOR];
$minor = $mirror_data->[$MIRROR_MINOR];
} elsif ($type == $TARGET_TYPE_MULTIPATH) {
# Use data of first device in list
my $multipath_data = $data->[0];
$major = $multipath_data->[$MULTIPATH_MAJOR];
$minor = $multipath_data->[$MULTIPATH_MINOR];
}
return ($major, $minor);
}
# create_temp_device_node(type, major, minor)
# Returns the name of a temporary device node.
sub create_temp_device_node($$$)
{
my ($type, $major, $minor) = @_;
my $path = "/dev";
my $name;
my $num;
for ($num = 0; $num < 100; $num++) {
$name = sprintf("$path/zipl-dm-temp-%02d", $num);
if (-e $name) {
next;
}
if (system("$mknod $name $type $major $minor --mode 0600 ".
"2>/dev/null")) {
next;
}
return $name;
}
die("Error: Could not create temporary device node in '$path'\n");
}
# get_blocksize(device)
# # Return blocksize in bytes for device.
sub get_blocksize($)
{
my ($dev) = @_;
my $blocksize;
local *HANDLE;
open(HANDLE, "$blockdev --getss $dev 2>/dev/null|") or
return undef;
$blocksize = <HANDLE>;
chomp($blocksize);
close(HANDLE);
return $blocksize;
}
# get_dasd_info(device)
# Returns (type, cylinders, heads, sectors)
sub get_dasd_info($)
{
my ($dev) = @_;
my $disk_type;
my $format;
my $cyl;
my $heads;
my $sectors;
my $type;
local *HANDLE;
open(HANDLE, "$dasdview -x -f $dev 2>/dev/null|") or
# dasdview returned with an error
return undef;
while (<HANDLE>) {
if (/^number of cylinders.*\s(\d+)\s*$/) {
$cyl = $1;
} elsif (/^tracks per cylinder.*\s(\d+)\s*$/) {
$heads = $1;
} elsif (/^blocks per track.*\s(\d+)\s*$/) {
$sectors = $1;
} elsif (/^type\s+:\s+(\S+)\s*$/) {
$disk_type = $1;
} elsif (/^format.*\s+dec\s(\d+)\s/) {
$format = $1;
}
}
close(HANDLE);
if (!defined($cyl) || !defined($heads) || !defined($sectors) ||
!defined($disk_type) || !defined($format)) {
# Unrecognized dadsview output format
return undef;
}
if ($disk_type eq "FBA") {
$type = $DEV_TYPE_FBA;
} elsif ($disk_type eq "ECKD") {
if ($format == 1) {
$type = $DEV_TYPE_LDL;
} elsif ($format == 2) {
$type = $DEV_TYPE_CDL;
}
}
return ($type, $cyl, $heads, $sectors);
}
# get_partition_start(major, minor)
# Return the partition offset of device.
sub get_partition_start($$)
{
my ($major, $minor) = @_;
my $dir = "/sys/dev/block/$major:$minor";
my $offset;
local *HANDLE;
return undef if (!-d $dir);
open(HANDLE, "<", "$dir/start") or return 0;
$offset = <HANDLE>;
close(HANDLE);
chomp($offset);
return $offset;
}
# is_dasd(type)
# Return whether disk with type is a DASD.
sub is_dasd($)
{
my ($type) = @_;
return ($type == $DEV_TYPE_CDL) || ($type == $DEV_TYPE_LDL) ||
($type == $DEV_TYPE_FBA);
}
# get_partition_base(type, major, minor)
# Return (major, minor) of the base device on which the partition is located.
sub get_partition_base($$$)
{
my ($type, $major, $minor) = @_;
if (is_dasd($type)) {
return ($major, $minor & ~$DASD_PARTN_MASK);
} else {
return ($major, $minor & ~$SCSI_PARTN_MASK);
}
}
# get_device_characteristics(major, minor)
# Returns (type, blocksize, geometry, bootsectors, partstart) for device.
sub get_device_characteristics($$)
{
my ($major, $minor) = @_;
my $dev;
my $blocksize;
my $type;
my $cyl;
my $heads;
my $sectors;
my $geometry;
my $bootsectors;
my $partstart;
$dev = create_temp_device_node("b", $major, $minor);
$blocksize = get_blocksize($dev);
if (!defined($blocksize)) {
unlink($dev);
die("Error: Could not get block size for ".
get_device_name($major, $minor)."\n");
}
($type, $cyl, $heads, $sectors) = get_dasd_info($dev);
if (defined($type)) {
$geometry = "$cyl,$heads,$sectors";
if ($type == $DEV_TYPE_CDL) {
# First track contains IPL records
$bootsectors = $blocksize * $sectors / $SECTOR_SIZE;
} elsif ($type == $DEV_TYPE_LDL) {
# First two blocks contain IPL records
$bootsectors = $blocksize * 2 / $SECTOR_SIZE;
} elsif ($type == $DEV_TYPE_FBA) {
# First block contains IPL records
$bootsectors = $blocksize / $SECTOR_SIZE;
}
} else {
# Assume SCSI if get_dasd_info failed
$type = $DEV_TYPE_SCSI;
# First block contains IPL records
$bootsectors = $blocksize / $SECTOR_SIZE;
}
$partstart = get_partition_start($major, $minor);
unlink($dev);
if (!defined($partstart)) {
die("Error: Could not determine partition start for ".
get_device_name($major, $minor)."\n");
}
# Convert partition start in sectors to blocks
$partstart = $partstart / ($blocksize / $SECTOR_SIZE);
return ($type, $blocksize, $geometry, $bootsectors, $partstart);
}
# get_type_name(type)
# Return textual representation of device type.
sub get_type_name($)
{
my ($type) = @_;
if ($type == $DEV_TYPE_CDL) {
return "CDL";
} elsif ($type == $DEV_TYPE_LDL) {
return "LDL";
} elsif ($type == $DEV_TYPE_FBA) {
return "FBA";
} elsif ($type == $DEV_TYPE_SCSI) {
return "SCSI";
}
return undef;
}
# check_for_mirror(index, target_list)
# Die if there is a mirror target between index and 0.
sub check_for_mirror($@)
{
my ($i, @target_list) = @_;
for (;$i >= 0; $i--) {
my $entry = $target_list[$i];
my ($major, $minor, $target) = @$entry;
if ($target->[$TARGET_TYPE] == $TARGET_TYPE_MIRROR) {
# IPL records are not mirrored.
die("Error: Unsupported setup: Block 0 is not ".
"mirrored in device '".
get_device_name($major, $minor)."'\n");
}
}
}
# get_target_base(bottom_major, bottom_minor, start, length, target_list)
# Return (major, minor) for the top most target in the target list that maps
# the region on (bottom_major, bottom_minor) defined by start and length at
# offset 0.
sub get_target_base($$$$@)
{
my ($bot_major, $bot_minor, $start, $length, @target_list) = @_;
my $entry;
my $top_major;
my $top_minor;
my $i;
# Pre-initialize with bottom major-minor
$top_major = $bot_major;
$top_minor = $bot_minor;
# Process all entries starting with the last one
for ($i = scalar(@target_list) - 1; $i >= 0; $i--) {
my $entry = $target_list[$i];
my ($major, $minor, $target) = @$entry;
if (($target->[$TARGET_START] != 0) ||
(get_target_start($target) != 0) ||
($target->[$TARGET_LENGTH] < $length)) {
last;
}
$top_major = $major;
$top_minor = $minor;
}
# Check for mirrorring between base device and fs device.
check_for_mirror($i, @target_list);
return ($top_major, $top_minor);
}
# get_device_name(major, minor)
# Return the name of the device specified by major and minor.
sub get_device_name($$)
{
my ($major, $minor) = @_;
my $name;
local *HANDLE;
$name = "$major:$minor";
open(HANDLE, "</proc/partitions") or goto out;
while (<HANDLE>) {
if (/^\s*(\d+)\s+(\d+)\s+\d+\s+(\S+)\s*$/) {
if (($major == $1) && ($minor == $2)) {
$name = $3;
last;
}
}
}
close(HANDLE);
out:
return $name;
}
sub check_tools()
{
system("$dmsetup --version &> /dev/null") >> 8 != 127
or die("Error: dmsetup not found\n");
system("$mknod --version &> /dev/null") >> 8 != 127
or die("Error: mknod not found\n");
system("$dasdview --version &> /dev/null") >> 8 != 127
or die("Error: dasdview not found\n");
system("$blockdev --version &> /dev/null") >> 8 != 127
or die("Error: blockdev not found\n");
return 0;
}