Files
s390-tools/dasdinfo/dasdinfo.c
Jan Höppner e581fa3fea dasdinfo: Fix false line breaks
Commit 61b60baf57 ("dasdinfo: Fix GCC 7 overflow warning") erroneously
introduced line breaks to the UID (-u) output. To be consistent with the
extended uid ouput code, keep the newley indroduced line breaks and
remove the additional one from the string split above instead.

Fixes: 61b60baf57 ("dasdinfo: Fix GCC 7 overflow warning")
Signed-off-by: Jan Höppner <hoeppner@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-12-06 16:00:16 +01:00

792 lines
19 KiB
C

/*
* dasdinfo - Display unique DASD ID, either UID or volser
*
* Copyright IBM Corp. 2007, 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "lib/util_base.h"
#include "lib/util_file.h"
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "lib/zt_common.h"
#define RD_BUFFER_SIZE 80
#define BLKSSZGET _IO(0x12,104)
#define DASD_IOCTL_LETTER 'D'
#define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,struct dasd_information)
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,struct dasd_information2)
#define TEMP_DEV_MAX_RETRIES 1000
static const struct util_prg prg = {
.desc = "Display DASD volume serial number and ID information",
.args = "-i BUSID | -b BLOCKDEV | -d DEVNODE",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2007,
},
UTIL_PRG_COPYRIGHT_END
}
};
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("DEVICE"),
{
.option = { "block", required_argument, NULL, 'b' },
.argument = "BLOCKDEV",
.desc = "Block device name, e.g. dasdb",
},
{
.option = { "devnode", required_argument, NULL, 'd' },
.argument = "DEVNODE",
.desc = "Device node, e.g. /dev/dasda",
},
{
.option = { "busid", required_argument, NULL, 'i' },
.argument = "BUSID",
.desc = "Bus ID, e.g. 0.0.e910",
},
UTIL_OPT_SECTION("OPTIONS"),
{
.option = { "label", no_argument, NULL, 'l' },
.desc = "Print DASD volume label (volser)",
},
{
.option = { "uid", no_argument, NULL, 'u' },
.desc = "Print DASD uid (without z/VM minidisk token)",
},
{
.option = { "extended-uid", no_argument, NULL, 'x' },
.desc = "Print DASD uid (including z/VM minidisk token)",
},
{
.option = { "all", no_argument, NULL, 'a' },
.desc = "Same as -u -x -l",
},
{
.option = { "export", no_argument, NULL, 'e' },
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
},
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
};
/* needed because ftw can not pass arbitrary arguments */
static char *searchbusid;
static char *busiddir;
struct volume_label {
char volkey[4];
char vollbl[4];
char volid[6];
} __attribute__ ((packed));
struct dasd_information2 {
unsigned int devno;
unsigned int real_devno;
unsigned int schid;
unsigned int cu_type : 16;
unsigned int cu_model : 8;
unsigned int dev_type : 16;
unsigned int dev_model : 8;
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len;
char type[4];
unsigned int status;
unsigned int label_block;
unsigned int FBA_layout;
unsigned int characteristics_size;
unsigned int confdata_size;
char characteristics[64];
char configuration_data[256];
unsigned int format;
unsigned int features;
unsigned int reserved0;
unsigned int reserved1;
unsigned int reserved2;
unsigned int reserved3;
unsigned int reserved4;
unsigned int reserved5;
unsigned int reserved6;
unsigned int reserved7;
};
struct dasd_information {
unsigned int devno;
unsigned int real_devno;
unsigned int schid;
unsigned int cu_type : 16;
unsigned int cu_model : 8;
unsigned int dev_type : 16;
unsigned int dev_model : 8;
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len;
char type[4];
unsigned int status;
unsigned int label_block;
unsigned int FBA_layout;
unsigned int characteristics_size;
unsigned int confdata_size;
char characteristics[64];
char configuration_data[256];
};
struct dasd_data
{
struct dasd_information2 dasd_info;
int dasd_info_version;
int blksize;
};
static char EBCtoASC[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
};
static char *dinfo_ebcdic_dec (char *source, char *target, int l)
{
int i;
for (i = 0; i < l; i++)
target[i]=EBCtoASC[(unsigned char)(source[i])];
return target;
}
static int dinfo_read_dasd_uid(char *uidfile, char *readbuf)
{
return util_file_read_line(readbuf, RD_BUFFER_SIZE, uidfile);
}
static int dinfo_read_dasd_vlabel (char *device, struct volume_label *vlabel,
char *readbuf)
{
struct dasd_data data;
struct volume_label tmp;
int vlsize = sizeof(struct volume_label);
unsigned long vlabel_start;
char vollbl[5];
int f;
char *space;
if ((f = open(device, O_RDONLY)) < 0) {
printf("Could not open device node.\n");
goto error;
}
if (ioctl(f, BLKSSZGET, &data.blksize) != 0) {
printf("Unable to figure out block size.\n");
goto error;
}
if (ioctl(f, BIODASDINFO2, &data.dasd_info) == 0)
data.dasd_info_version = 2;
else {
if (ioctl(f, BIODASDINFO, &data.dasd_info) != 0) {
printf("Unable to figure out DASD informations.\n");
goto error;
}
}
vlabel_start = data.dasd_info.label_block * data.blksize;
if (lseek(f, vlabel_start, SEEK_SET) < 0)
goto error;
bzero(vlabel, vlsize);
if (read(f, vlabel, vlsize) != vlsize) {
printf("Could not read volume label.\n");
goto error;
}
if (data.dasd_info.FBA_layout) {
bzero(&tmp, vlsize);
memcpy(&tmp, vlabel, vlsize);
memcpy(vlabel->vollbl, &tmp, vlsize-4);
}
close(f);
bzero(readbuf, 7);
bzero(vollbl, 5);
strncpy(vollbl, vlabel->vollbl, 4);
dinfo_ebcdic_dec(vollbl, vollbl, 4);
if ((strncmp(vollbl, "VOL1", 4) == 0) ||
(strncmp(vollbl, "LNX1", 4) == 0) ||
(strncmp(vollbl, "CMS1", 4) == 0)) {
strncpy(readbuf, vlabel->volid, 6);
dinfo_ebcdic_dec(readbuf, readbuf, 6);
space = strchr(readbuf,' ');
if (space)
*space = 0;
} else
strcpy(readbuf, "");
return 0;
error:
close(f);
return -1;
}
static void *dinfo_malloc(size_t size)
{
void *result;
result = malloc(size);
if (result == NULL) {
printf("Could not allocate %lld bytes of memory",
(unsigned long long) size);
}
return result;
}
static char *dinfo_make_path(char *dirname, char *filename)
{
char *result;
size_t len;
len = strlen(dirname) + strlen(filename) + 2;
result = (char *) dinfo_malloc(len);
if (result == NULL)
return NULL;
sprintf(result, "%s/%s", dirname, filename);
return result;
}
static int dinfo_create_devnode(dev_t dev, char **devno)
{
char *result;
char * pathname[] = { "/dev", getenv("TMPDIR"), "/tmp",
getenv("HOME"), "." , "/"};
char filename[] = "dasdinfo0000";
mode_t mode;
unsigned int path;
int retry;
int rc;
int fd;
mode = S_IFBLK | S_IRWXU;
/* Try several locations for the temporary device node. */
for (path=0; path < sizeof(pathname) / sizeof(pathname[0]); path++) {
if (pathname[path] == NULL)
continue;
for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
sprintf(filename, "dasdinfo%04d", retry);
result = dinfo_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_RDONLY);
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);
}
}
printf("Error: Unable to create temporary device node");
return -1;
}
static void dinfo_free_devnode(char *device)
{
if (remove(device)) {
printf("Warning: Could not remove "
"temporary file %s", device);
}
}
static int dinfo_extract_dev(dev_t *dev, char *str)
{
char tmp[RD_BUFFER_SIZE];
char *p = NULL;
int ma, mi;
bzero(tmp, RD_BUFFER_SIZE);
strncpy(tmp, str, RD_BUFFER_SIZE);
if ((p = strchr(tmp, ':')) == NULL) {
printf("Error: unable to extract major/minor\n");
return -1;
}
*p = '\0';
ma = atoi(tmp);
mi = atoi(p + sizeof(char));
*dev = makedev(ma, mi);
return 0;
}
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
{
char *readbuf = NULL;
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) {
printf("Error: Not enough memory to allocate readbuffer\n");
return -1;
}
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
"/sys/block/%s/dev", blockdev) < 0)
return -1;
if (dinfo_extract_dev(dev, readbuf) != 0)
return -1;
return 0;
}
static int
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
int tflag, struct FTW *ftwbuf)
{
char *tempdir;
char linkdir[128];
ssize_t i;
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
strlen(searchbusid)) != 0))
return FTW_CONTINUE;
/*
* ensure that the found entry is a busid and not a
* subchannel ID
* for large systems subchannel IDs may look like busids
*/
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
return -1;
i = readlink(tempdir, linkdir, 128);
free(tempdir);
if ((i < 0) || (i >= 128))
return -1;
/* append '\0' because readlink returns non zero terminated string */
tempdir[i+1] = '\0';
if (strstr(linkdir, "dasd") == NULL)
return FTW_CONTINUE;
free(busiddir);
busiddir = strdup(fpath);
if (busiddir == NULL)
return -1;
return FTW_STOP;
}
static int
dinfo_find_entry(const char *dir, const char *searchstring,
char type, char **result)
{
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
directory = opendir(dir);
if (directory == NULL)
return -1;
while ((dir_entry = readdir(directory)) != NULL) {
/* compare if the found entry has exactly the same name
and type as searched */
if ((strncmp(dir_entry->d_name, searchstring,
strlen(searchstring)) == 0)
&& (dir_entry->d_type & type)) {
*result = strdup(dir_entry->d_name);
if (*result == NULL)
goto out;
closedir(directory);
return 0; /* found */
}
}
out:
closedir(directory);
return -1; /* nothing found or error */
}
static int
dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
{
int flags = FTW_PHYS; /* do not follow links */
int rc = -1;
char *tempdir = NULL;
char *result = NULL;
char *sysfsdir = "/sys/devices/";
/* dinfo_is_devnode needs to know the busid */
searchbusid = busid;
if (nftw(sysfsdir, dinfo_is_busiddir, 200, flags) != FTW_STOP)
goto out;
/*
* new sysfs: busid directory contains a directory 'block'
* which contains a directory 'dasdXXX'
*/
rc = dinfo_find_entry(busiddir, "block", DT_DIR, &result);
if (rc == 0) {
if (asprintf(&tempdir, "%s/%s/", busiddir, result) < 0) {
rc = -1;
goto out2;
}
rc = dinfo_find_entry(tempdir, "dasd", DT_DIR, blkdev);
} else {
/*
* old sysfs: entry for busiddir contain a link
* 'block:dasdXXX'
*/
rc = dinfo_find_entry(busiddir, "block:", DT_LNK, &result);
if (rc != 0)
goto out2;
*blkdev = strdup(strchr(result, ':') + 1);
if (*blkdev == NULL)
rc = -1;
}
out:
free(tempdir);
out2:
free(busiddir);
free(result);
return rc;
}
static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
{
struct stat stat_buffer;
char stat_dev[RD_BUFFER_SIZE];
char *readbuf;
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
int rc = 0;
if (stat(devnode, &stat_buffer) != 0) {
printf("Error: could not stat %s\n", devnode);
return -1;
}
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
minor(stat_buffer.st_rdev));
if ((directory = opendir("/sys/block/")) == NULL) {
printf("Error: could not open directory /sys/block\n");
return -1;
}
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) {
printf("Error: Not enough memory to allocate readbuffer\n");
return -1;
}
while ((dir_entry = readdir(directory)) != NULL) {
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
"/sys/block/%s/dev",
dir_entry->d_name) < 0)
continue;
if (strncmp(stat_dev, readbuf,
MAX(strlen(stat_dev), strlen(readbuf)-1)) == 0) {
rc = snprintf(*uidfile, RD_BUFFER_SIZE,
"/sys/block/%s/device/uid",
dir_entry->d_name);
if (rc >= RD_BUFFER_SIZE) {
fprintf(stderr,
"Error: Device name was truncated\n");
return -1;
}
break;
}
}
closedir(directory);
return 0;
}
int main(int argc, char * argv[])
{
struct utsname uname_buf;
int version, release;
char *uidfile = NULL;
char *device = NULL;
char *readbuf = NULL;
dev_t dev;
int export = 0;
int c;
int print_uid = 0;
int print_extended_uid = 0;
int print_vlabel = 0;
char *blockdev = NULL;
char *busid = NULL;
char *devnode = NULL;
struct volume_label vlabel;
char *srchuid;
int i, rc = 0;
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
break;
switch (c) {
case 'a':
print_uid = 1;
print_vlabel = 1;
print_extended_uid = 1;
break;
case 'u':
print_uid = 1;
break;
case 'x':
print_extended_uid = 1;
break;
case 'l':
print_vlabel = 1;
break;
case 'i':
busid=strdup(optarg);
break;
case 'b':
blockdev=strdup(optarg);
break;
case 'd':
devnode=strdup(optarg);
break;
case 'e':
export = 1;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
exit(EXIT_SUCCESS);
case 'v':
util_prg_print_version();
exit(EXIT_SUCCESS);
default:
fprintf(stderr, "Try 'dasdinfo --help' for more "
"information.\n");
exit(1);
}
}
uname(&uname_buf);
sscanf(uname_buf.release, "%d.%d", &version,&release);
if (strcmp(uname_buf.sysname,"Linux") ||
version < 2 || (version == 2 && release < 6)) {
printf("%s %d.%d is not supported\n", uname_buf.sysname,
version,release);
exit(1);
}
if (!busid && !blockdev && !devnode) {
printf("Error: please specify a device using either -b, -i "
"or -d\n");
exit(1);
}
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode)) {
printf("Error: please specify device only once, either -b, -i "
"or -d\n");
exit(1);
}
if (!print_uid && !print_extended_uid && !print_vlabel) {
printf("Error: no action specified (e.g. -u)\n");
exit(1);
}
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
if (!(readbuf && uidfile))
exit(1);
/* try to read the uid attribute */
if (busid) {
sprintf(uidfile,"/sys/bus/ccw/devices/%s/uid", busid);
} else if (blockdev) {
sprintf(uidfile,"/sys/block/%s/device/uid", blockdev);
} else if (devnode) {
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
goto error;
}
if (export) {
printf("ID_BUS=ccw\n");
printf("ID_TYPE=disk\n");
}
if (print_uid) {
if (dinfo_read_dasd_uid(uidfile, readbuf) == 0) {
/* look for the 4th '.' and cut there */
srchuid = readbuf - 1;
for (i = 0; i < 4; ++i) {
srchuid = index(srchuid + 1, '.');
if (!srchuid)
break;
}
if (srchuid) {
srchuid[0] = '\0';
}
if (export) {
printf("ID_UID=%s\n", readbuf);
} else
printf("%s\n", readbuf);
if (!print_vlabel && !print_extended_uid)
goto out;
}
}
if (print_extended_uid) {
if (dinfo_read_dasd_uid(uidfile, readbuf) == 0) {
if (export) {
printf("ID_XUID=%s\n", readbuf);
} else
printf("%s\n", readbuf);
if (!print_vlabel)
goto out;
}
}
/* there is no uid, try to read the volume serial */
if (busid) {
char *blockdev_name = NULL;
if (dinfo_get_blockdev_from_busid(busid, &blockdev_name) != 0)
goto error;
if (dinfo_get_dev_from_blockdev(blockdev_name, &dev) != 0)
goto error;
if (dinfo_create_devnode(dev, &device) != 0)
goto error;
free(blockdev_name);
} else if (blockdev) {
if (dinfo_get_dev_from_blockdev(blockdev, &dev) != 0)
goto error;
if (dinfo_create_devnode(dev, &device) != 0)
goto error;
} else if (devnode) {
device = dinfo_malloc(RD_BUFFER_SIZE);
if (!device)
exit(1);
strcpy(device, devnode);
}
if (dinfo_read_dasd_vlabel(device, &vlabel, readbuf) == 0) {
if (export) {
printf("ID_SERIAL=%s\n",readbuf);
} else
printf("%s\n", readbuf);
goto out;
}
error:
printf("Error: could not read unique DASD ID\n");
rc = 1;
out:
if (device && (busid || blockdev))
dinfo_free_devnode(device);
free(uidfile);
free(device);
free(readbuf);
exit(rc);
}