Files
s390-tools/zipl/src/zipl_helper.device-mapper.c
Stefan Haberland 08bc0a947a zipl: fix some warnings in zipl_helper
Fix some warnings found by sparse.
Declare one function static and change static variables to defines.

Signed-off-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2018-05-07 13:42:03 +02:00

1071 lines
25 KiB
C

/*
*
* zipl_helper.device-mapper: print zipl parameters for a device-mapper device
*
* Copyright IBM Corp. 2009, 2017
* Copyright Red Hat Inc. 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.
*
*/
#include <errno.h>
#include <limits.h>
#include <linux/limits.h>
#include <locale.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/dasd_base.h"
#include "lib/util_base.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_list.h"
#include "lib/util_path.h"
#include "lib/util_proc.h"
#define WARN(...) \
fprintf(stderr, "Warning: " __VA_ARGS__)
#define ERR(...) \
fprintf(stderr, "Error: " __VA_ARGS__)
struct target_status {
char *path;
char status;
struct util_list_node list;
};
struct target_data {
dev_t device;
unsigned long start;
struct util_list_node list;
};
struct target {
unsigned long start;
unsigned long length;
unsigned short type;
struct util_list *data;
struct util_list_node list;
};
struct target_entry {
dev_t device;
struct target *target;
struct util_list_node list;
};
struct physical_device {
dev_t device;
unsigned long offset;
struct util_list *target_list;
};
struct device_characteristics {
unsigned short type;
unsigned int blocksize;
unsigned long bootsectors;
unsigned long partstart;
struct hd_geometry geo;
};
/* From include/linux/fs.h */
#define BDEVNAME_SIZE 32
/* Constants */
#define SECTOR_SIZE 512
#define DASD_PARTN_MASK 0x03
#define SCSI_PARTN_MASK 0x0f
#define CHREIPL_HELPER "chreipl_helper.device-mapper"
/* Internal constants */
enum dev_type {
DEV_TYPE_CDL = 0,
DEV_TYPE_LDL,
DEV_TYPE_FBA,
DEV_TYPE_SCSI
};
enum target_type {
TARGET_TYPE_LINEAR = 0,
TARGET_TYPE_MIRROR,
TARGET_TYPE_MULTIPATH
};
static void get_type_name(char *name, unsigned short type)
{
switch (type) {
case DEV_TYPE_SCSI:
strcpy(name, "SCSI");
break;
case DEV_TYPE_CDL:
strcpy(name, "CDL");
break;
case DEV_TYPE_FBA:
strcpy(name, "FBA");
break;
case DEV_TYPE_LDL:
strcpy(name, "LDL");
break;
default:
name[0] = '\0';
WARN("Unrecognized dev type %d\n", type);
}
}
static FILE *exec_cmd_and_get_out_stream(const char *fmt, ...)
{
FILE *stream;
va_list ap;
char *cmd;
va_start(ap, fmt);
util_vasprintf(&cmd, fmt, ap);
va_end(ap);
stream = popen(cmd, "r");
if (stream == NULL)
WARN("'%s' failed\n", cmd);
free(cmd);
return stream;
}
static struct target_data *target_data_new(unsigned int maj, unsigned int min,
unsigned int start)
{
struct target_data *td = util_malloc(sizeof(struct target_data));
td->device = makedev(maj, min);
td->start = start;
return td;
}
static void target_data_free(struct target_data *td)
{
free(td);
}
static void target_data_list_free(struct util_list *data)
{
struct target_data *td, *n;
util_list_iterate_safe(data, td, n) {
util_list_remove(data, td);
target_data_free(td);
}
util_list_free(data);
}
static struct target *target_new(unsigned long start, unsigned long length,
unsigned short type, struct util_list *data)
{
struct target *entry = util_malloc(sizeof(struct target));
entry->start = start;
entry->length = length;
entry->type = type;
entry->data = data;
return entry;
}
static void target_free(struct target *target)
{
target_data_list_free(target->data);
free(target);
}
static unsigned long target_get_start(struct target *target)
{
struct target_data *td = util_list_start(target->data);
return td->start;
}
static void target_get_major_minor(struct target *target, unsigned int *major,
unsigned int *minor)
{
struct target_data *td = util_list_start(target->data);
*major = major(td->device);
*minor = minor(td->device);
}
static struct target_entry *target_entry_new(dev_t dev, struct target *target)
{
struct target_entry *te = util_malloc(sizeof(struct target_entry));
te->device = dev;
te->target = target;
return te;
}
static void target_entry_free(struct target_entry *entry)
{
target_free(entry->target);
free(entry);
}
static struct target_entry *target_list_get_first_by_type(struct util_list *target_list,
unsigned short type)
{
struct target_entry *te;
util_list_iterate(target_list, te) {
if (te->target->type == type)
return te;
}
return NULL;
}
static void target_list_free(struct util_list *target_list)
{
struct target_entry *te, *n;
util_list_iterate_safe(target_list, te, n) {
util_list_remove(target_list, te);
target_entry_free(te);
}
util_list_free(target_list);
}
static void get_device_name(char *devname, dev_t dev)
{
struct util_proc_part_entry entry;
if (util_proc_part_get_entry(dev, &entry) == 0) {
strcpy(devname, entry.name);
util_proc_part_free_entry(&entry);
} else {
sprintf(devname, "%u:%u", major(dev), minor(dev));
}
}
static int create_temp_device_node(char *name, unsigned int major,
unsigned int minor)
{
const char path_base[] = "/dev";
char buf[PATH_MAX];
int n;
for (n = 0; n < 100; n++) {
snprintf(buf, sizeof(buf), "%s/zipl-dm-temp-%02d", path_base, n);
if (util_path_exists(buf))
continue;
if (mknod(buf, S_IFBLK, makedev(major, minor)) != 0)
continue;
strcpy(name, buf);
return 0;
}
ERR("Could not create temporary device node in '%s'\n", path_base);
return -1;
}
static long get_partition_start(unsigned int major, unsigned int minor)
{
unsigned long val;
if (!util_path_is_dir("/sys/dev/block/%u:%u", major, minor))
return -1;
if (util_file_read_ul(&val, 10, "/sys/dev/block/%u:%u/start",
major, minor) != 0) {
return 0;
}
return val;
}
static int get_dev_characteristics(struct device_characteristics *dc, dev_t dev)
{
char devname[PATH_MAX];
dasd_information2_t info;
long pstart;
if (create_temp_device_node(devname, major(dev), minor(dev)) != 0)
return -1;
if (dasd_get_blocksize(devname, &dc->blocksize) != 0) {
ERR("Could not get block size for '%s'\n", devname);
goto err;
}
if (dasd_get_info(devname, &info) != 0) {
/* Assume SCSI if dasdinfo failed */
dc->type = DEV_TYPE_SCSI;
/* First block contains IPL records */
dc->bootsectors = dc->blocksize / SECTOR_SIZE;
} else {
if (dasd_get_geo(devname, &dc->geo) != 0) {
ERR("Could not get geo info for '%s'\n", devname);
goto err;
}
if (strncmp(info.type, "FBA", 3) == 0) {
dc->type = DEV_TYPE_FBA;
dc->bootsectors = dc->blocksize / SECTOR_SIZE;
} else if (strncmp(info.type, "ECKD", 4) == 0) {
if (info.format == 1) {
dc->type = DEV_TYPE_LDL;
dc->bootsectors = dc->blocksize * 2 /
SECTOR_SIZE;
} else if (info.format == 2) {
dc->type = DEV_TYPE_CDL;
dc->bootsectors = dc->blocksize *
dc->geo.sectors / SECTOR_SIZE;
}
}
}
pstart = get_partition_start(major(dev), minor(dev));
if (pstart < 0) {
ERR("Could not determine partition start for '%s'\n",
devname);
goto err;
}
dc->partstart = pstart / (dc->blocksize / SECTOR_SIZE);
unlink(devname);
return 0;
err:
unlink(devname);
return -1;
}
static struct util_list *get_linear_data(const char *devname, char *args)
{
unsigned int major, minor, start;
struct util_list *data;
if (sscanf(args, "%u:%u %u", &major, &minor, &start) < 3) {
ERR("Unrecognized device-mapper table format for device '%s'\n",
devname);
return NULL;
}
data = util_list_new(struct target_data, list);
util_list_add_tail(data, target_data_new(major, minor, start));
return data;
}
#define STR_TOKEN_OR_GOTO(string, tok, label) \
do { \
tok = strtok(string, " "); \
if (tok == NULL) { \
goto label; \
} \
} while (0)
#define NEXT_STR_TOKEN_OR_GOTO(tok, label) \
STR_TOKEN_OR_GOTO(NULL, tok, label)
#define INT_TOKEN_OR_GOTO(string, tok, label) \
do { \
char *tp = strtok(string, " "); \
if (tp == NULL) { \
goto label; \
} \
errno = 0; \
tok = strtol(tp, NULL, 10); \
if (errno != 0) { \
goto label; \
} \
} while (0)
#define NEXT_INT_TOKEN_OR_GOTO(tok, label) \
INT_TOKEN_OR_GOTO(NULL, tok, label)
#define SKIP_TOKEN_OR_GOTO(string, label) \
do { \
if (strtok(string, " ") == NULL) { \
goto label; \
} \
} while (0)
#define SKIP_NEXT_TOKEN_OR_GOTO(label) \
SKIP_TOKEN_OR_GOTO(NULL, label)
#define SKIP_NEXT_TOKENS_OR_GOTO(count, label) \
do { \
int i; \
for (i = 0; i < count; i++) { \
SKIP_NEXT_TOKEN_OR_GOTO(label); \
} \
} while (0)
/*
* There is no kernel documentation for the mirror target. Parameters obtained
* from Linux sources: drivers/md/dm-log.c and drivers/md/dm-raid1.c
*
* <starting_sector> <length> mirror \
* <log_type> <#log_args> <log_arg1>...<log_argN> \
* <#devs> <device_name_1> <offset_1>...<device name N> <offset N> \
* <#features> <feature_1>...<feature_N>
*/
static struct util_list *get_mirror_data(const char *devname, char *args)
{
struct util_list *data = util_list_new(struct target_data, list);
long nlogs, ndevs, nfeats, base_offset = -1;
SKIP_TOKEN_OR_GOTO(args, out); /* log_type */
NEXT_INT_TOKEN_OR_GOTO(nlogs, out); /* #log_args */
SKIP_NEXT_TOKENS_OR_GOTO(nlogs, out); /* log_args* */
NEXT_INT_TOKEN_OR_GOTO(ndevs, out);
for (; ndevs > 0; ndevs--) {
unsigned int major, minor;
long offset;
char *name;
NEXT_STR_TOKEN_OR_GOTO(name, out);
if (sscanf(name, "%u:%u", &major, &minor) < 2)
goto out;
NEXT_INT_TOKEN_OR_GOTO(offset, out);
if ((base_offset >= 0) && (offset != base_offset)) {
ERR("Unsupported setup: Mirror target on device '%s' "
"contains entries with varying sector offsets\n",
devname);
goto out2;
} else {
base_offset = offset;
}
util_list_add_tail(data, target_data_new(major, minor, offset));
}
NEXT_INT_TOKEN_OR_GOTO(nfeats, out);
SKIP_NEXT_TOKENS_OR_GOTO(nfeats, out);
return data;
out:
ERR("Unrecognized device-mapper table format for device '%s'\n", devname);
out2:
target_data_list_free(data);
return NULL;
}
static struct target_status *target_status_new(const char *path, char status)
{
struct target_status *ts = util_malloc(sizeof(struct target_status));
ts->path = util_strdup(path);
ts->status = status;
return ts;
}
static void target_status_free(struct target_status *ts)
{
free(ts->path);
free(ts);
}
static void status_list_free(struct util_list *status)
{
struct target_status *ts, *n;
util_list_iterate_safe(status, ts, n) {
util_list_remove(status, ts);
target_status_free(ts);
}
util_list_free(status);
}
static char status_list_get_status(struct util_list *status, const char *node)
{
struct target_status *ts;
util_list_iterate(status, ts) {
if (strcmp(ts->path, node) == 0)
return ts->status;
}
return 'F';
}
static struct util_list *get_multipath_status(const char *devname)
{
struct util_list *status;
int len, failed = 0;
char *line = NULL;
size_t n = 0;
FILE *fp;
fp = exec_cmd_and_get_out_stream("dmsetup status /dev/%s 2>/dev/null",
devname);
if (fp == NULL) {
ERR("No paths found for '%s'\n", devname);
return NULL;
}
status = util_list_new(struct target_status, list);
while (getline(&line, &n, fp) != -1) {
char *token = NULL;
long cnt, ngr;
/* 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
*/
STR_TOKEN_OR_GOTO(line, token, out);
SKIP_NEXT_TOKEN_OR_GOTO(out); /* length */
NEXT_STR_TOKEN_OR_GOTO(token, out); /* dtype */
if (strcmp(token, "multipath") != 0)
continue;
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #mp_feature_args */
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out); /* mp_feature_args* */
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #handler_status_args */
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out); /* handler_status_args* */
NEXT_INT_TOKEN_OR_GOTO(ngr, out);
SKIP_NEXT_TOKEN_OR_GOTO(out); /* ign */
for (; ngr > 0; ngr--) {
long npaths, nsa;
NEXT_STR_TOKEN_OR_GOTO(token, out); /* group_state */
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #ps_status_args */
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out); /* ps_status_args* */
NEXT_INT_TOKEN_OR_GOTO(npaths, out);
NEXT_INT_TOKEN_OR_GOTO(nsa, out);
for (; npaths > 0; npaths--) {
struct target_status *ts;
char *path, *active;
/* Fetch single path description */
NEXT_STR_TOKEN_OR_GOTO(path, out);
NEXT_STR_TOKEN_OR_GOTO(active, out);
ts = target_status_new(path, active[0]);
util_list_add_tail(status, ts);
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* fail_cnt */
failed += (active[0] != 'A');
SKIP_NEXT_TOKENS_OR_GOTO(nsa, out); /* selector_args* */
}
}
}
free(line);
pclose(fp);
len = util_list_len(status);
if (len == 0) {
ERR("No paths found for '%s'\n", devname);
goto out;
} else if (failed == len) {
ERR("All paths for '%s' failed\n", devname);
goto out;
} else if (failed > 0) {
WARN("There are one or more failed paths for device '%s'\n",
devname);
}
return status;
out:
free(line);
pclose(fp);
status_list_free(status);
return NULL;
}
static struct util_list *get_multipath_data(const char *devname, char *args)
{
struct util_list *data = util_list_new(struct target_data, list);
struct util_list *status = get_multipath_status(devname);
long cnt, pgroups;
if (status == NULL)
goto out_status;
INT_TOKEN_OR_GOTO(args, cnt, out); /* #feat */
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out); /* feats* */
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #handlers */
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out); /* handlers* */
NEXT_INT_TOKEN_OR_GOTO(pgroups, out);
SKIP_NEXT_TOKEN_OR_GOTO(out); /* pathgroup */
for (; pgroups > 0; pgroups--) {
long npaths;
SKIP_NEXT_TOKEN_OR_GOTO(out); /* path_selector */
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #selector_args */
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out); /* selector_args* */
NEXT_INT_TOKEN_OR_GOTO(npaths, out);
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #np_args */
for (; npaths > 0; npaths--) {
unsigned int major, minor;
char *path;
NEXT_STR_TOKEN_OR_GOTO(path, out);
if (sscanf(path, "%u:%u", &major, &minor) < 2)
goto out;
if (status_list_get_status(status, path) == 'A') {
struct target_data *td;
td = target_data_new(major, minor, 0);
util_list_add_tail(data, td);
}
SKIP_NEXT_TOKENS_OR_GOTO(cnt, out);
}
}
status_list_free(status);
return data;
out:
status_list_free(status);
out_status:
target_data_list_free(data);
ERR("Unrecognized device-mapper table format for device '%s'\n", devname);
return NULL;
}
static void table_free(struct util_list *table)
{
struct target *target, *n;
util_list_iterate_safe(table, target, n) {
util_list_remove(table, target);
target_free(target);
}
util_list_free(table);
}
static void filter_table(struct util_list *table, unsigned int start,
unsigned int length)
{
struct target *target, *n;
util_list_iterate_safe(table, target, n) {
if (!(((target->start + target->length - 1) >= start) &&
(target->start <= (start + length - 1)))) {
util_list_remove(table, target);
target_free(target);
}
}
}
/*
* Return list of target devices
*/
static struct util_list *get_table(dev_t dev)
{
char devname[BDEVNAME_SIZE];
struct util_list *table;
char *line = NULL;
size_t n = 0;
FILE *fp;
table = util_list_new(struct target, list);
fp = exec_cmd_and_get_out_stream("dmsetup table -j %u -m %u 2>/dev/null",
major(dev), minor(dev));
if (fp == NULL)
return table;
get_device_name(devname, dev);
while (getline(&line, &n, fp) != -1) {
char *type = NULL, *args = NULL;
struct util_list *data = NULL;
unsigned long start, length;
unsigned short ttype;
if (sscanf(line, "%lu %lu %ms %m[a-zA-Z0-9_: -]",
&start, &length, &type, &args) < 4) {
ERR("Unrecognized device-mapper table format for device '%s'\n",
devname);
goto out;
}
if (strcmp(type, "linear") == 0) {
data = get_linear_data(devname, args);
ttype = TARGET_TYPE_LINEAR;
} else if (strcmp(type, "mirror") == 0) {
data = get_mirror_data(devname, args);
ttype = TARGET_TYPE_MIRROR;
} else if (strcmp(type, "multipath") == 0) {
data = get_multipath_data(devname, args);
ttype = TARGET_TYPE_MULTIPATH;
} else {
ERR("Unsupported setup: Unsupported device-mapper "
"target type '%s' for device '%s'\n",
type, devname);
}
free(type);
free(args);
if (data == NULL)
goto out;
util_list_add_tail(table, target_new(start, length, ttype, data));
}
free(line);
pclose(fp);
return table;
out:
free(line);
pclose(fp);
table_free(table);
return NULL;
}
static bool is_dasd(unsigned short type)
{
return (type == DEV_TYPE_CDL) || (type == DEV_TYPE_LDL) ||
(type == DEV_TYPE_FBA);
}
static int get_physical_device(struct physical_device *pd, dev_t dev,
const char *directory)
{
struct util_list *target_list = NULL;
struct util_list *table = NULL;
unsigned int start, length;
struct target *target;
table = get_table(dev);
if (table == NULL || util_list_is_empty(table)) {
char devname[BDEVNAME_SIZE];
get_device_name(devname, dev);
ERR("Could not retrieve device-mapper information for device "
"'%s'\n", devname);
if (table != NULL)
table_free(table);
return -1;
}
target = util_list_start(table);
/* Filesystem must be on a single dm target */
if (util_list_next(table, target) != NULL) {
ERR("Unsupported setup: Directory '%s' is located on a "
"multi-target device-mapper device\n", directory);
table_free(table);
return -1;
}
util_list_remove(table, target);
table_free(table);
target_list = util_list_new(struct target_entry, list);
util_list_add_head(target_list, target_entry_new(dev, target));
start = target->start;
length = target->length;
while (true) {
unsigned int major, minor;
/* Convert fs_start to offset on parent dm device */
start += target_get_start(target);
target_get_major_minor(target, &major, &minor);
table = get_table(makedev(major, minor));
/* Found non-dm device */
if (table == NULL || util_list_is_empty(table)) {
pd->device = makedev(major, minor);
pd->offset = start;
pd->target_list = target_list;
if (table != NULL)
table_free(table);
return 0;
}
/* Get target in parent table which contains filesystem.
* We are interested only in targets between
* [start,start+length-1].
*/
filter_table(table, start, length);
target = util_list_start(table);
if (target == NULL || util_list_next(table, target) != NULL) {
ERR("Unsupported setup: Could not map directory '%s' "
"to a single physical device\n", directory);
table_free(table);
target_list_free(target_list);
return -1;
}
util_list_remove(table, target);
util_list_add_head(target_list,
target_entry_new(makedev(major, minor), target));
table_free(table);
/* Convert fs_start to offset on parent target */
start -= target->start;
}
}
static int get_major_minor(dev_t *dev, const char *filename)
{
struct stat buf;
if (stat(filename, &buf) != 0) {
ERR("Could not stat '%s'", filename);
return -1;
}
*dev = buf.st_dev;
return 0;
}
static int get_physical_device_dir(struct physical_device *pd,
const char *directory)
{
dev_t dev;
if (get_major_minor(&dev, directory) != 0)
return -1;
return get_physical_device(pd, dev, directory);
}
static int get_target_base(dev_t *base, dev_t bottom, unsigned int length,
struct util_list *target_list)
{
struct target_entry *te, *tm;
dev_t top = bottom;
util_list_iterate(target_list, te) {
if ((te->target->start != 0) ||
(target_get_start(te->target) != 0) ||
(te->target->length < length)) {
break;
}
top = te->device;
}
/* Check for mirroring between base device and fs device */
for (tm = te; tm != NULL; tm = util_list_next(target_list, tm)) {
if (tm->target->type == TARGET_TYPE_MIRROR) {
char name[BDEVNAME_SIZE];
get_device_name(name, tm->device);
ERR("Unsupported setup: Block 0 is not mirrored in "
"device '%s'\n", name);
return -1;
}
}
*base = top;
return 0;
}
static inline dev_t get_partition_base(unsigned short type, dev_t dev)
{
return makedev(major(dev), minor(dev) &
(is_dasd(type) ? ~DASD_PARTN_MASK : ~SCSI_PARTN_MASK));
}
static int extract_major_minor_from_cmdline(char *argv[], unsigned int *major,
unsigned int *minor)
{
if (sscanf(argv[1], "%u:%u", major, minor) != 2) {
return -1;
}
return 0;
}
static bool toolname_is_chreipl_helper(const char *toolname)
{
int clen = strlen(CHREIPL_HELPER);
int tlen = strlen(toolname);
if (tlen < clen)
return false;
return strcmp(toolname + tlen - clen, CHREIPL_HELPER) == 0;
}
static void print_usage(const char *toolname)
{
fprintf(stderr, "%s <major:minor of target device>", toolname);
if (!toolname_is_chreipl_helper(toolname))
fprintf(stderr, " or <target directory>");
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
struct device_characteristics dc = {0};
const char *toolname = argv[0];
struct physical_device pd;
unsigned int major, minor;
char *directory = NULL;
char type_name[8];
dev_t base;
int res;
if (argc <= 1)
goto usage;
if (setlocale(LC_ALL, "C") == NULL) {
ERR("Could not use standard locale\n");
exit(EXIT_FAILURE);
}
if (toolname_is_chreipl_helper(toolname)) {
if (extract_major_minor_from_cmdline(argv, &major, &minor) != 0)
goto usage;
if (get_physical_device(&pd, makedev(major, minor), argv[1]) != 0)
exit(EXIT_FAILURE);
printf("%u:%u\n", major(pd.device), minor(pd.device));
target_list_free(pd.target_list);
exit(EXIT_SUCCESS);
}
directory = argv[1];
if (extract_major_minor_from_cmdline(argv, &major, &minor) == 0)
res = get_physical_device(&pd, makedev(major, minor), directory);
else
res = get_physical_device_dir(&pd, directory);
if (res != 0)
exit(EXIT_FAILURE);
if (get_dev_characteristics(&dc, pd.device) != 0)
goto error;
/* Handle partitions */
if (dc.partstart > 0) {
struct device_characteristics ndc = {0};
struct target_entry *mirror;
/* Only the partition of the physical device is mapped so only
* the physical device can provide access to the boot record
*/
base = get_partition_base(dc.type, pd.device);
/* Check for mirror */
mirror = target_list_get_first_by_type(pd.target_list,
TARGET_TYPE_MIRROR);
if (mirror != NULL) {
char name[BDEVNAME_SIZE];
get_device_name(name, mirror->device);
/* IPL records are not mirrored */
ERR("Unsupported setup: Block 0 is not mirrored in "
"device '%s'\n", name);
goto error;
}
/* Adjust filesystem offset */
pd.offset += (dc.partstart * (dc.blocksize / SECTOR_SIZE));
dc.partstart = 0;
/* Update device geometry */
get_dev_characteristics(&ndc, base);
dc.geo = ndc.geo;
} else {
/* All of the device is mapped, so the base device is the
* top most dm device which provides access to boot sectors
*/
if (get_target_base(&base, pd.device, dc.bootsectors,
pd.target_list) != 0)
goto error;
}
/* Check for valid offset of filesystem */
if ((pd.offset % (dc.blocksize / SECTOR_SIZE)) != 0) {
ERR("File system not aligned on physical block size\n");
goto error;
}
target_list_free(pd.target_list);
/* Print resulting information */
printf("targetbase=%u:%u\n", major(base), minor(base));
get_type_name(type_name, dc.type);
printf("targettype=%s\n", type_name);
if (dc.geo.cylinders != 0 && dc.geo.heads != 0 && dc.geo.sectors != 0) {
printf("targetgeometry=%d,%d,%d\n",
dc.geo.cylinders, dc.geo.heads, dc.geo.sectors);
}
printf("targetblocksize=%d\n", dc.blocksize);
printf("targetoffset=%lu\n", (pd.offset / (dc.blocksize / SECTOR_SIZE)));
exit(EXIT_SUCCESS);
error:
if (pd.target_list != NULL)
target_list_free(pd.target_list);
exit(EXIT_FAILURE);
usage:
print_usage(toolname);
exit(EXIT_FAILURE);
}