mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf9fd47918 | ||
|
|
5aec1dc2a1 | ||
|
|
cf730a9632 | ||
|
|
73bab8e1a1 | ||
|
|
f97d048643 | ||
|
|
d8c630e5f4 | ||
|
|
45132ce1ee | ||
|
|
57a797350c | ||
|
|
75e3afb6a0 | ||
|
|
7915c9257b | ||
|
|
6014d07cb1 | ||
|
|
5d2ebe5301 | ||
|
|
ab510cff39 | ||
|
|
8c06dea4e5 | ||
|
|
0efacac5b8 | ||
|
|
6ea645b345 | ||
|
|
331b54d573 | ||
|
|
7e7a77675d | ||
|
|
dc2e439550 | ||
|
|
75d4317f20 | ||
|
|
58a7462f65 | ||
|
|
7c7e10ed8f | ||
|
|
0c1a63ce5e | ||
|
|
6825645a21 | ||
|
|
e764f460c4 | ||
|
|
63089835b0 | ||
|
|
e4cd42900f | ||
|
|
7f033938b8 | ||
|
|
f2dee9f542 | ||
|
|
c7a255fcd9 | ||
|
|
be7b854969 | ||
|
|
05a0f8e0eb | ||
|
|
43fcb694bf | ||
|
|
8be43a1f16 | ||
|
|
7b4a05e5a3 | ||
|
|
9911a95144 | ||
|
|
9c56255a70 | ||
|
|
a20cabccc4 | ||
|
|
f67758160e | ||
|
|
e84287e76e | ||
|
|
0c78add815 | ||
|
|
94b7a8f68d | ||
|
|
4036e80b26 | ||
|
|
a236180ad8 | ||
|
|
a9684b9154 | ||
|
|
6d31760872 | ||
|
|
b0dcc61ed5 | ||
|
|
a3e37953fe | ||
|
|
abf18cb711 | ||
|
|
22a4d45e3b | ||
|
|
e693173eef | ||
|
|
11bfa1d3c8 | ||
|
|
3ed8ab4e2a | ||
|
|
5a0c93443c | ||
|
|
b0c7965234 | ||
|
|
cdb23f8d22 | ||
|
|
60bda7ed0d | ||
|
|
4ff6519961 | ||
|
|
90dc65659f | ||
|
|
ec9c67189e | ||
|
|
951bd1ae54 | ||
|
|
ed6e3b7270 | ||
|
|
79ce120553 | ||
|
|
b45b564681 | ||
|
|
c8d0fca36c | ||
|
|
bbd88f26c9 | ||
|
|
79bfa818ed | ||
|
|
299144264d | ||
|
|
a69470d7e0 | ||
|
|
b26dbfe832 | ||
|
|
d95dc6d698 | ||
|
|
847f16f632 | ||
|
|
bc053a975a | ||
|
|
e9c030f202 | ||
|
|
dffd41943e | ||
|
|
58189b8786 | ||
|
|
b165500b69 | ||
|
|
5f2ddad6a8 | ||
|
|
28deb03178 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -69,6 +69,7 @@ zconf/scm/lsscm
|
||||
zconf/zcrypt/chzcrypt
|
||||
zconf/zcrypt/lszcrypt
|
||||
zconf/zcrypt/zcryptctl
|
||||
zconf/zcrypt/zcryptstats
|
||||
zdev/src/chzdev
|
||||
zdev/src/chzdev_usage.c
|
||||
zdev/src/lszdev
|
||||
|
||||
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,5 +1,52 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __v2.9.0 (2019-05-21)__
|
||||
|
||||
For Linux kernel version: 5.0 / 5.1
|
||||
|
||||
Add new tool:
|
||||
- zcryptstats: Add zcryptstats to display usage statistics of
|
||||
IBM Crypto Express adapters
|
||||
|
||||
Changes of existing tools:
|
||||
- lszfcp: New command line option to show module parameters
|
||||
- lszfcp: Sdev attributes for scsi_disk, block, integrity, queue, iosched
|
||||
- lszfcp: Add new output marker for non-good SCSI devices (luns)
|
||||
- lszfcp: Add new output marker for non-good fc_rports
|
||||
- lszfcp: Clean up whitespace (mixed indentation, trailing)
|
||||
- lschp: Add support for specifying a CHPID
|
||||
- zipl: Add secure boot capabilities
|
||||
- zkey: Add common passphrase options for cryptsetup and crypttab
|
||||
- zkey: Add batch-mode option to cryptsetup and zkey-cryptsetup
|
||||
- libu2s: Remove the entire library and provide more robust functionality
|
||||
in libdasd and libutil instead
|
||||
|
||||
Bug Fixes:
|
||||
- lszfcp: Allow to show zfcp_units without associated SCSI device
|
||||
- lszfcp: Attribute details for: css, zfcp_port, zfcp_unit
|
||||
- lszfcp: Allow to also enumerate FCP device that have never been online
|
||||
- lszfcp: Fix error message if no zfcp-attached SCSI device found
|
||||
- lszfcp: Fix to show defunct FCP devices again
|
||||
- lszfcp: Fix to show non-good target ports again
|
||||
- lszfcp: Fix missing block & sg device output without CONFIG_SYSFS_DEPRECATED
|
||||
- lszfcp: New command line option for extended output format
|
||||
- zfcpdbf: Warn about ambiguous payload records with dup reqid & payarea
|
||||
- zpcictl: Check for regular directory to prevent possible buffer overflow
|
||||
|
||||
* __v2.8.0 (2019-02-15)__
|
||||
|
||||
For Linux kernel version: 4.20
|
||||
|
||||
Changes of existing tools:
|
||||
- Switch to using /run directory instead of the legacy /var/run
|
||||
- zkey: Add --pbkdf pbkdf2 to generated cryptsetup luksFormat command
|
||||
- zdsfs: Add online VTOC refresh
|
||||
- pkey: Support autoloading kernel pkey module
|
||||
|
||||
Bug Fixes:
|
||||
- zkey: Avoid EPERM on key change if user is not owner of key file
|
||||
- cpumf/cpumf_helper: Always return list reference for --sfb-size
|
||||
|
||||
* __v2.7.1 (2018-12-13)__
|
||||
|
||||
For Linux kernel version: 4.19
|
||||
|
||||
2
Makefile
2
Makefile
@@ -3,7 +3,7 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
|
||||
# Include common definitions
|
||||
include common.mak
|
||||
|
||||
LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw libvmcp
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp
|
||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
|
||||
@@ -129,6 +129,7 @@ Package contents
|
||||
or show encryption state of attached LUNs.
|
||||
- lszcrypt: Show Information about zcrypt devices and configuration.
|
||||
- chzcrypt: Modify the zcrypt configuration.
|
||||
- zcryptstats: Display usage statistics of IBM Crypto Express adapters.
|
||||
- znetconf: List and configure network devices for s390 network adapters.
|
||||
- cio_ignore: Query and modify the contents of the CIO device driver
|
||||
blacklist.
|
||||
@@ -284,6 +285,9 @@ Build and runtime requirements for specific tools
|
||||
In the following more details on the build an runtime requirements of
|
||||
the different tools are provided:
|
||||
|
||||
* dbginfo.sh:
|
||||
The tar package is required to archive collected data.
|
||||
|
||||
* osasnmpd:
|
||||
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
|
||||
installed, before building the osasnmpd subagent.
|
||||
|
||||
@@ -5,8 +5,8 @@ COMMON_INCLUDED = true
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 7
|
||||
PATCHLEVEL = 1
|
||||
RELEASE = 9
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
export S390_TOOLS_RELEASE
|
||||
@@ -331,10 +331,6 @@ $(rootdir)/libzds/libzds.a: $(rootdir)/libzds
|
||||
$(MAKE) -C $(rootdir)/libzds/ libzds.a
|
||||
.PHONY: $(rootdir)/libzds
|
||||
|
||||
$(rootdir)/libu2s/libu2s.a: $(rootdir)/libu2s
|
||||
$(MAKE) -C $(rootdir)/libu2s/ libu2s.a
|
||||
.PHONY: $(rootdir)/libu2s
|
||||
|
||||
$(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump
|
||||
$(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a
|
||||
.PHONY: $(rootdir)/libvmdump
|
||||
|
||||
@@ -110,7 +110,7 @@ The default command is --print all.
|
||||
.
|
||||
.SH FILES
|
||||
.nf
|
||||
/var/run/cpacfstatsd_socket
|
||||
/run/cpacfstatsd_socket
|
||||
.fi
|
||||
.
|
||||
.SH RETURN VALUE
|
||||
|
||||
@@ -78,8 +78,8 @@ struct msg_answer {
|
||||
|
||||
#define BACKLOG 10
|
||||
|
||||
#define SOCKET_FILE "/var/run/cpacfstatsd_socket"
|
||||
#define PID_FILE "/var/run/cpacfstatsd.pid"
|
||||
#define SOCKET_FILE "/run/cpacfstatsd_socket"
|
||||
#define PID_FILE "/run/cpacfstatsd.pid"
|
||||
|
||||
#define CPACFSTATS_GROUP "cpacfstats"
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ restart the daemon to ensure correct summing of the per-CPU performance
|
||||
counters.
|
||||
|
||||
The starting daemon first checks for any stale pid file
|
||||
/var/run/cpacfstatsd.pid. If this file exists, and the process ID in the
|
||||
/run/cpacfstatsd.pid. If this file exists, and the process ID in the
|
||||
file belongs to an active process, an error message is printed to the
|
||||
console and the program terminates.
|
||||
|
||||
@@ -81,8 +81,8 @@ daemon startup and initialization failures.
|
||||
|
||||
.SH FILES
|
||||
.nf
|
||||
/var/run/cpacfstatsd_socket
|
||||
/var/run/cpacfstatsd.pid
|
||||
/run/cpacfstatsd_socket
|
||||
/run/cpacfstatsd.pid
|
||||
.fi
|
||||
|
||||
.SH RETURN VALUE
|
||||
|
||||
@@ -51,10 +51,11 @@ sub cpumf_get_sfb_size()
|
||||
my $val = "0,0";
|
||||
my $SFBSIZE;
|
||||
|
||||
return $val unless open($SFBSIZE, '<', $CPUM_SFB_SIZE);
|
||||
$val = <$SFBSIZE>;
|
||||
chomp($val);
|
||||
close($SFBSIZE);
|
||||
if (open($SFBSIZE, '<', $CPUM_SFB_SIZE)) {
|
||||
$val = <$SFBSIZE>;
|
||||
chomp($val);
|
||||
close($SFBSIZE);
|
||||
}
|
||||
|
||||
return [split /,/, $val];
|
||||
}
|
||||
|
||||
@@ -161,8 +161,8 @@ int is_online(int cpuid)
|
||||
retval = 1;
|
||||
if (state == 0)
|
||||
retval = 0;
|
||||
fclose(filp);
|
||||
}
|
||||
fclose(filp);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -220,8 +220,8 @@ int cpu_is_configured(int cpuid)
|
||||
retval = 1;
|
||||
if (state == 0)
|
||||
retval = 0;
|
||||
fclose(filp);
|
||||
}
|
||||
fclose(filp);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#define NAME "cpuplugd"
|
||||
#define MAX_HISTORY 100
|
||||
#define PIDFILE "/var/run/cpuplugd.pid"
|
||||
#define PIDFILE "/run/cpuplugd.pid"
|
||||
#define LOCKFILE "/var/lock/cpuplugd.lock"
|
||||
#define PROCINFO_LINE 512
|
||||
#define CPUSTAT_SIZE 1024
|
||||
|
||||
@@ -4,7 +4,6 @@ all: dasdfmt
|
||||
|
||||
libs = $(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
dasdfmt: dasdfmt.o $(libs)
|
||||
|
||||
@@ -1394,7 +1394,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname,
|
||||
if ((info->verbosity > 0) || !info->withoutprompt || info->testmode)
|
||||
dasdfmt_print_info(info, devname, vlabel, cylinders, heads, p);
|
||||
|
||||
count = u2s_get_host_access_count(devname);
|
||||
count = dasd_get_host_access_count(devname);
|
||||
if (info->force_host) {
|
||||
if (count > 1) {
|
||||
ERRMSG_EXIT(EXIT_FAILURE,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
include ../common.mak
|
||||
|
||||
libs = $(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a \
|
||||
libs = $(rootdir)/libutil/libutil.a \
|
||||
$(rootdir)/libdasd/libdasd.a
|
||||
|
||||
all: dasdinfo
|
||||
|
||||
@@ -7,7 +7,6 @@ all: dasdview
|
||||
libs = $(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libzds/libzds.a \
|
||||
$(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
dasdview: dasdview.o $(libs)
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
#include "lib/dasd_base.h"
|
||||
#include "lib/dasd_sys.h"
|
||||
#include "lib/libzds.h"
|
||||
#include "lib/u2s.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_sys.h"
|
||||
#include "lib/vtoc.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
@@ -183,7 +183,7 @@ dasdview_get_info(dasdview_info_t *info)
|
||||
else
|
||||
info->hw_cylinders = characteristics->no_cyl;
|
||||
|
||||
if (u2s_getbusid(info->device, info->busid) == -1)
|
||||
if (util_sys_get_dev_addr(info->device, info->busid) != 0)
|
||||
info->busid_valid = 0;
|
||||
else
|
||||
info->busid_valid = 1;
|
||||
@@ -1741,7 +1741,7 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info)
|
||||
" rc=%d\n", rc);
|
||||
exit(-1);
|
||||
}
|
||||
rc = lzds_dasd_read_rawvtoc(info->dasd);
|
||||
rc = lzds_dasd_alloc_rawvtoc(info->dasd);
|
||||
if (rc == EINVAL) {
|
||||
zt_error_print("dasdview: Cannot read VTOC because disk does"
|
||||
" not contain valid VOL1 label.\n",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define DASDVIEW_H
|
||||
|
||||
#include <limits.h>
|
||||
#include "lib/u2s.h"
|
||||
|
||||
/********************************************************************************
|
||||
* SECTION: Definitions needed for DASD-API (see dasd.h)
|
||||
@@ -109,7 +108,7 @@ typedef struct dasdview_info
|
||||
int f8c;
|
||||
int f9c;
|
||||
|
||||
char busid[U2S_BUS_ID_SIZE];
|
||||
char busid[DASD_BUS_ID_SIZE];
|
||||
int busid_valid;
|
||||
int raw_track_access;
|
||||
struct zdsroot *zdsroot;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
DAEMON=cpacfstatsd
|
||||
DAEMON_PATH=/usr/sbin/cpacfstatsd
|
||||
RUN_PID_FILE=/var/run/cpacfstatsd.pid
|
||||
RUN_PID_FILE=/run/cpacfstatsd.pid
|
||||
RETVAL=0
|
||||
OPTIONS=""
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
DAEMON=cpuplugd
|
||||
DAEMON_PATH=/usr/sbin/cpuplugd
|
||||
CONFIG_FILE=/etc/cpuplugd.conf
|
||||
RUN_PID_FILE=/var/run/cpuplugd.pid
|
||||
RUN_PID_FILE=/run/cpuplugd.pid
|
||||
RETVAL=0
|
||||
OPTIONS="-c $CONFIG_FILE"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ DUMP_CONFIG_FILE=/etc/sysconfig/dumpconf
|
||||
CMDFULL=$0
|
||||
CMD="dumpconf"
|
||||
LOCKFILE=/var/lock/$CMD
|
||||
PIDFILE=/var/run/$CMD.pid
|
||||
PIDFILE=/run/$CMD.pid
|
||||
ERRMSG="Check $DUMP_CONFIG_FILE!"
|
||||
|
||||
RETVAL=0
|
||||
|
||||
@@ -20,8 +20,8 @@ PROCD=mon_procd
|
||||
FSSTATD_PATH=/usr/sbin/$FSSTATD
|
||||
PROCD_PATH=/usr/sbin/$PROCD
|
||||
CONFIG_FILE=/etc/sysconfig/$DAEMON
|
||||
FSSTATD_PID_FILE=/var/run/$FSSTATD.pid
|
||||
PROCD_PID_FILE=/var/run/$PROCD.pid
|
||||
FSSTATD_PID_FILE=/run/$FSSTATD.pid
|
||||
PROCD_PID_FILE=/run/$PROCD.pid
|
||||
|
||||
# source function library
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
2
etc/modules-load.d/s390-pkey.conf
Normal file
2
etc/modules-load.d/s390-pkey.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
# Load protected key support module on s390 early at boot
|
||||
pkey
|
||||
@@ -3,7 +3,6 @@ include ../common.mak
|
||||
libs = $(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libzds/libzds.a \
|
||||
$(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
all: fdasd
|
||||
|
||||
@@ -935,7 +935,7 @@ static void fdasd_verify_device(fdasd_anchor_t *anc, char *name)
|
||||
fdasd_error(anc, device_verification_failed, err_str);
|
||||
}
|
||||
|
||||
count = u2s_get_host_access_count(name);
|
||||
count = dasd_get_host_access_count(name);
|
||||
if (anc->force_host) {
|
||||
if (count > 1) {
|
||||
snprintf(err_str, ERROR_STRING_SIZE,
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* A bus id of a DASD is 8 characters long. E.g. 0.0.4711 */
|
||||
#define DASD_BUS_ID_SIZE 9
|
||||
|
||||
typedef struct dasd_information2_t {
|
||||
unsigned int devno; /* S/390 devno */
|
||||
unsigned int real_devno; /* for aliases */
|
||||
@@ -170,6 +173,10 @@ struct hd_geometry {
|
||||
#define BIODASDDISABLE _IO(DASD_IOCTL_LETTER, 0)
|
||||
/* Enable the volume (for Linux) */
|
||||
#define BIODASDENABLE _IO(DASD_IOCTL_LETTER, 1)
|
||||
/* Reserve the device for the current LPAR */
|
||||
#define BIODASDRSRV _IO(DASD_IOCTL_LETTER, 2)
|
||||
/* Release the device for the current LPAR */
|
||||
#define BIODASDRLSE _IO(DASD_IOCTL_LETTER, 3)
|
||||
/* Get information on a dasd device (enhanced) */
|
||||
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t)
|
||||
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
|
||||
@@ -203,5 +210,7 @@ int dasd_get_geo(const char *device, struct hd_geometry *geo);
|
||||
int dasd_get_info(const char *device, dasd_information2_t *info);
|
||||
int dasd_is_ro(const char *device, bool *ro);
|
||||
int dasd_reread_partition_table(const char *device, int ntries);
|
||||
int dasd_disk_reserve(const char *device);
|
||||
int dasd_disk_release(const char *device);
|
||||
|
||||
#endif /* LIB_DASD_BASE_H */
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
#define LIB_DASD_SYS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "u2s.h"
|
||||
|
||||
int dasd_sys_raw_track_access(char *);
|
||||
int dasd_reset_chpid(char *, char *);
|
||||
int dasd_get_host_access_count(char *device);
|
||||
|
||||
#endif /* LIB_DASD_SYS_H */
|
||||
|
||||
@@ -112,6 +112,8 @@
|
||||
*/
|
||||
#define LIB_LIBZDS_H
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "vtoc.h"
|
||||
|
||||
|
||||
@@ -328,11 +330,74 @@ struct pds_member_entry {
|
||||
*/
|
||||
struct zdsroot;
|
||||
|
||||
/**
|
||||
* @struct raw_vtoc
|
||||
* @brief The VTOC is a directory of data sets on one DASD
|
||||
*
|
||||
* As the VTOC is the data area on the DASD that describes all data sets,
|
||||
* this library will often have to refer to the various records in the VTOC.
|
||||
* To make this more efficient, we will read the whole VTOC once and identify
|
||||
* all elements (DSCBs). The raw data of the VTOC tracks and the index to the
|
||||
* DSCBs is stored.
|
||||
*/
|
||||
struct raw_vtoc {
|
||||
/** @brief The raw track data */
|
||||
char *rawdata;
|
||||
/** @brief This size of the raw track data in bytes */
|
||||
unsigned long long rawdatasize;
|
||||
/** @brief An array with pointers to the various DSCBs in the rawdata */
|
||||
char **vtocindex;
|
||||
/** @brief Number of entries in the index */
|
||||
unsigned int vtocindexcount;
|
||||
/** @brief Number of records per VTOC track
|
||||
*
|
||||
* @note While the DS4DEVDT field in the format 4 DSCB names the number
|
||||
* if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1
|
||||
* for record 0.
|
||||
*/
|
||||
unsigned int vtoc_rec_per_track;
|
||||
/** @brief The track number at which the vtoc begins on the DASD */
|
||||
unsigned int vtoctrackoffset;
|
||||
/** @brief Start record of VTOC.
|
||||
*
|
||||
* The rawdata contains full tracks. This is the number of the first
|
||||
* record that actually belongs to the VTOC
|
||||
*/
|
||||
unsigned int vtocrecno;
|
||||
/** @brief The DASD this vtoc was read from */
|
||||
struct dasd *dasd;
|
||||
/** @brief Detailed error messages in case of a problem */
|
||||
struct errorlog *log;
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct dasd
|
||||
* @brief Represents one physical device, may have a vtoc
|
||||
*/
|
||||
struct dasd;
|
||||
struct dasd {
|
||||
/** @brief List head used to store a list of DASDs in struct zdsroot */
|
||||
struct util_list_node list;
|
||||
/** @brief Name of the block device, e.g. /dev/dasde */
|
||||
char *device;
|
||||
/** @brief File descriptor for the block device.
|
||||
*
|
||||
* The device is kept open for as along as the library uses it.
|
||||
* This lets the system know that the device is still in use.
|
||||
*/
|
||||
int inusefd;
|
||||
/* @brief where to find the volume label */
|
||||
unsigned int label_block;
|
||||
/** @brief Device geometry. How many cylinders does the DASD have. */
|
||||
unsigned int cylinders;
|
||||
/** @brief Device geometry. How many heads does the DASD have. */
|
||||
unsigned int heads;
|
||||
/** @brief The VTOC data that has been read from this device */
|
||||
struct raw_vtoc *rawvtoc;
|
||||
/** @brief The volume label that has been read from this device */
|
||||
volume_label_t *vlabel;
|
||||
/** @brief Detailed error messages in case of a problem */
|
||||
struct errorlog *log;
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct dasditerator
|
||||
@@ -350,12 +415,6 @@ struct dasditerator;
|
||||
*/
|
||||
struct dasdhandle;
|
||||
|
||||
/**
|
||||
* @struct raw_vtoc
|
||||
* @brief The VTOC is a directory of data sets on one dasd
|
||||
*/
|
||||
struct raw_vtoc;
|
||||
|
||||
/**
|
||||
* @struct dscbiterator
|
||||
* @brief allows to iterate over all DSCBs in a vtoc
|
||||
@@ -572,7 +631,13 @@ int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel);
|
||||
* @brief Read the vtoc data from device. The data as stored as part
|
||||
* of the struct dasd.
|
||||
*/
|
||||
int lzds_dasd_read_rawvtoc(struct dasd *dasd);
|
||||
int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *vtoc);
|
||||
|
||||
/**
|
||||
* @brief Read the vtoc data from device. The data as stored as part
|
||||
* of the struct dasd.
|
||||
*/
|
||||
int lzds_dasd_alloc_rawvtoc(struct dasd *dasd);
|
||||
|
||||
/**
|
||||
* @brief Get the previously read raw_vtoc data.
|
||||
@@ -787,6 +852,8 @@ int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root,
|
||||
struct dasd *dasd);
|
||||
|
||||
|
||||
void lzds_dslist_free(struct zdsroot *root);
|
||||
|
||||
|
||||
/** @} */ /* end of group libzds_functions_high */
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright IBM Corp. 2004, 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.
|
||||
*
|
||||
* History of changes (starts July 2004)
|
||||
* 2004-07-02 initial
|
||||
*/
|
||||
|
||||
#ifndef LIB_U2S_H
|
||||
#define LIB_U2S_H
|
||||
|
||||
#define U2S_BUS_ID_SIZE 32
|
||||
|
||||
int u2s_getbusid(char *, char *);
|
||||
int u2s_read_attribute(char *, char *, char *, size_t);
|
||||
int u2s_get_host_access_count(char *);
|
||||
|
||||
#endif /* LIB_U2S_H */
|
||||
@@ -68,5 +68,8 @@ const char *util_rec_get(struct util_rec *rec, const char *key);
|
||||
|
||||
void util_rec_print_hdr(struct util_rec *rec);
|
||||
void util_rec_print(struct util_rec *rec);
|
||||
void util_rec_print_separator(struct util_rec *rec);
|
||||
|
||||
void util_rec_set_indent(struct util_rec *rec, int indent);
|
||||
|
||||
#endif /** LIB_UTIL_REC_H @} */
|
||||
|
||||
17
include/lib/util_sys.h
Normal file
17
include/lib/util_sys.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @defgroup util_sys_h util_sys: SysFS interface
|
||||
* @{
|
||||
* @brief Work with SysFS
|
||||
*
|
||||
* Copyright IBM Corp. 2019
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef LIB_UTIL_SYS_H
|
||||
#define LIB_UTIL_SYS_H
|
||||
|
||||
int util_sys_get_dev_addr(const char *dev, char *addr);
|
||||
|
||||
#endif /** LIB_UTIL_SYS_H @} */
|
||||
@@ -261,3 +261,42 @@ int dasd_reread_partition_table(const char *device, int ntries)
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reserve DASD disk.
|
||||
*
|
||||
* @param[in] device node device node's name
|
||||
*
|
||||
* @retval 0 in case of success
|
||||
* @retval errno in case of failure
|
||||
*
|
||||
*/
|
||||
int dasd_disk_reserve(const char *device)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = dasd_open_device(device, O_RDONLY);
|
||||
RUN_IOCTL(fd, BIODASDRSRV, NULL);
|
||||
dasd_close_device(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release DASD disk
|
||||
*
|
||||
* @param[in] device node device node's name
|
||||
*
|
||||
* @retval 0 in case of success
|
||||
* @retval errno in case of failure
|
||||
*/
|
||||
int dasd_disk_release(const char *device)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = dasd_open_device(device, O_RDONLY);
|
||||
RUN_IOCTL(fd, BIODASDRLSE, NULL);
|
||||
dasd_close_device(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/dasd_base.h"
|
||||
#include "lib/dasd_sys.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_sys.h"
|
||||
|
||||
/**
|
||||
* Get raw-track access mode status
|
||||
@@ -31,22 +35,24 @@
|
||||
*/
|
||||
int dasd_sys_raw_track_access(char *devnode)
|
||||
{
|
||||
char busid[9];
|
||||
char path[47];
|
||||
char busid[DASD_BUS_ID_SIZE];
|
||||
char *path;
|
||||
FILE *fp;
|
||||
int rc;
|
||||
|
||||
if (u2s_getbusid(devnode, busid))
|
||||
if (util_sys_get_dev_addr(devnode, busid) != 0)
|
||||
return 0;
|
||||
|
||||
sprintf(path, "/sys/bus/ccw/devices/%s/raw_track_access", busid);
|
||||
|
||||
path = util_path_sysfs("bus/ccw/devices/%s/raw_track_access", busid);
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = fgetc(fp) - '0';
|
||||
fclose(fp);
|
||||
free(path);
|
||||
|
||||
return (rc == 1) ? 1 : 0;
|
||||
}
|
||||
@@ -55,15 +61,17 @@ int dasd_sys_raw_track_access(char *devnode)
|
||||
int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
{
|
||||
unsigned int val;
|
||||
char path[40];
|
||||
int count, i;
|
||||
char *path;
|
||||
FILE *fp;
|
||||
|
||||
sprintf(path, "/sys/bus/ccw/devices/%s/../chpids", busid);
|
||||
path = util_path_sysfs("bus/ccw/devices/%s/../chpids", busid);
|
||||
*mask = 0;
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
free(path);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
count = fscanf(fp, " %x", &val);
|
||||
@@ -75,6 +83,7 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
*mask = 0x80 >> i;
|
||||
}
|
||||
fclose(fp);
|
||||
free(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -102,22 +111,25 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
int dasd_reset_chpid(char *devnode, char *chpid_char)
|
||||
{
|
||||
unsigned int chpid;
|
||||
char path[41];
|
||||
char busid[9];
|
||||
char busid[DASD_BUS_ID_SIZE];
|
||||
int mask, rc;
|
||||
char *endptr;
|
||||
char *path;
|
||||
FILE *fp;
|
||||
|
||||
if (u2s_getbusid(devnode, busid))
|
||||
if (util_sys_get_dev_addr(devnode, busid) != 0)
|
||||
return ENODEV;
|
||||
|
||||
if (!chpid_char) {
|
||||
sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid);
|
||||
path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid);
|
||||
fp = fopen(path, "w");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
free(path);
|
||||
return ENODEV;
|
||||
}
|
||||
fprintf(fp, "%s", "all\n");
|
||||
fclose(fp);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -132,12 +144,45 @@ int dasd_reset_chpid(char *devnode, char *chpid_char)
|
||||
if (!mask)
|
||||
return ENOENT;
|
||||
|
||||
sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid);
|
||||
path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid);
|
||||
fp = fopen(path, "w");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
free(path);
|
||||
return ENODEV;
|
||||
}
|
||||
fprintf(fp, "%02x", mask);
|
||||
fclose(fp);
|
||||
free(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read amount of host with access to \p device
|
||||
*
|
||||
* The \p device can be any valid relative or absolute path to a DASD device
|
||||
* node, for example:
|
||||
*
|
||||
* - /dev/dasda
|
||||
* - /dev/disk/by-path/ccw-0.0.bf20
|
||||
*
|
||||
* @param[in] device Device node of interest
|
||||
*
|
||||
* @retval n Number of hosts with access to \p device
|
||||
* @retval 0 Value could not be determined
|
||||
*/
|
||||
int dasd_get_host_access_count(char *device)
|
||||
{
|
||||
char busid[9];
|
||||
char *path;
|
||||
long value;
|
||||
|
||||
if (!util_sys_get_dev_addr(device, busid))
|
||||
return 0;
|
||||
|
||||
path = util_path_sysfs("bus/ccw/devices/%s/host_access_count", busid);
|
||||
util_file_read_l(&value, 10, path);
|
||||
free(path);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
include ../common.mak
|
||||
|
||||
lib = libu2s.a
|
||||
|
||||
all: $(lib)
|
||||
|
||||
objects = u2s.o
|
||||
|
||||
$(lib): $(objects)
|
||||
|
||||
install: all
|
||||
|
||||
clean:
|
||||
rm -f *.o $(lib)
|
||||
334
libu2s/u2s.c
334
libu2s/u2s.c
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright IBM Corp. 2004, 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <wait.h>
|
||||
|
||||
#include "lib/u2s.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#define DEV_BUFFER_LENGTH 20
|
||||
#define PATH_BUFFER_LENGTH 256
|
||||
#define BUSIDSIZE 9
|
||||
|
||||
#define BLOCKPATH "/sys/block/"
|
||||
#define DEVICE_LINK "device"
|
||||
#define DEV_ATTRIBUTE "dev"
|
||||
|
||||
|
||||
/*
|
||||
* Helper function that expects a file name and returns 1 if this
|
||||
* is a directory or 0 otherwise.
|
||||
*/
|
||||
static int isdir(char *name) {
|
||||
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(name, &statbuf) < 0)
|
||||
return 0;
|
||||
return S_ISDIR(statbuf.st_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function that expects a directory name in sysfs of the form
|
||||
* /sys/block/<devname>/ or /sys/block/<devname>/<partname>/.
|
||||
* It will try to read the file "dev" in this directory and compare
|
||||
* it's contents with the given dev string of the form <major>:<minor>.
|
||||
* Trailing white space (newline) is ignored.
|
||||
* The buffer name is expected to be long enough to hold the additional "dev".
|
||||
* Returns 1 if the directory matches dev, 0 otherwise.
|
||||
*/
|
||||
static int check_directory(char *name, char *dev) {
|
||||
|
||||
char buffer[DEV_BUFFER_LENGTH];
|
||||
char *end;
|
||||
int fd;
|
||||
ssize_t count;
|
||||
int dev_attr_len, dev_parm_len;
|
||||
unsigned int namelen;
|
||||
|
||||
namelen = strlen(name);
|
||||
if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEV_ATTRIBUTE))
|
||||
return 0;
|
||||
end = name + namelen;
|
||||
strcpy(end, DEV_ATTRIBUTE);
|
||||
fd = open(name, O_RDONLY);
|
||||
*end = 0;
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
count = read(fd, buffer, DEV_BUFFER_LENGTH);
|
||||
close(fd);
|
||||
if (count < 0)
|
||||
return 0;
|
||||
dev_attr_len = strspn(buffer, "1234567890:");
|
||||
dev_parm_len = strlen(dev);
|
||||
if (dev_attr_len != dev_parm_len )
|
||||
return 0;
|
||||
return (strncmp(dev, buffer, dev_parm_len) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function that expects a directory name in sysfs of the form
|
||||
* /sys/block/<devname>/. It will try to read a link "device"
|
||||
* in this directory and extract the busid, which is the last part
|
||||
* of that link. The buffer name is expected to be long enough
|
||||
* to hold the additional "device".
|
||||
* name: block device path in sysfs.
|
||||
* busid: buffer in which the busid string will be returned
|
||||
* returns 0 for successful operation and -1 in case of an error.
|
||||
*/
|
||||
static int extract_busid(char *name, char *busid) {
|
||||
|
||||
int count;
|
||||
unsigned int namelen;
|
||||
char linkbuffer[PATH_BUFFER_LENGTH];
|
||||
char *start, *end;
|
||||
size_t len;
|
||||
|
||||
namelen = strlen(name);
|
||||
if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEVICE_LINK))
|
||||
return 0;
|
||||
end = name + namelen;
|
||||
strcpy(end, DEVICE_LINK);
|
||||
count = readlink(name, linkbuffer, PATH_BUFFER_LENGTH - 1);
|
||||
if (count < 0)
|
||||
return -1;
|
||||
linkbuffer[count] = 0;
|
||||
start = strrchr(linkbuffer, '/');
|
||||
if (!start)
|
||||
return -1;
|
||||
start++;
|
||||
len = util_strlcpy(busid, start, BUSIDSIZE);
|
||||
if (len >= BUSIDSIZE)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function that makes some basic checks on a directory entry.
|
||||
* The function checks if there is still enough space left in the buffer
|
||||
* for the new string, excludes '.' and '..', and verifies that the entry
|
||||
* is actually a directory.
|
||||
* buffer: the beginning of the name buffer
|
||||
* oldend: the current end of the string in the name buffer
|
||||
* dir: the dirent in question
|
||||
* returns: a pointer to the new end of the string in buffer or NULL if
|
||||
* one of the checks failed
|
||||
*/
|
||||
|
||||
static char *append_if_directory(char *buffer, char *oldend, struct dirent *dir) {
|
||||
|
||||
char *newend;
|
||||
int oldlength, dirlength;
|
||||
|
||||
if (strcmp(dir->d_name, ".") == 0 ||
|
||||
strcmp(dir->d_name, "..") == 0)
|
||||
return NULL;
|
||||
oldlength = strlen(buffer);
|
||||
dirlength = strlen(dir->d_name);
|
||||
if (PATH_BUFFER_LENGTH < oldlength + dirlength + 2)
|
||||
return NULL;
|
||||
strcpy(oldend, dir->d_name);
|
||||
if (!isdir(buffer)) {
|
||||
*oldend = 0;
|
||||
return NULL;
|
||||
}
|
||||
newend = oldend + dirlength;
|
||||
strcpy(newend, "/");
|
||||
newend++;
|
||||
|
||||
return newend;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function that searches for a specific block device and returns
|
||||
* it's busid
|
||||
* dev: <major>:<minor> of the device
|
||||
* busid: buffer in which the busid string will be returned
|
||||
* returns 0 for successful operation and -1 in case of an error.
|
||||
*/
|
||||
static int find_busid_in_sysfs(char *dev, char *busid) {
|
||||
|
||||
DIR *blockdir, *diskdir;
|
||||
struct dirent *blockde, *diskde;
|
||||
int found = 0;
|
||||
char namebuffer[PATH_BUFFER_LENGTH];
|
||||
char *blockend, *diskend = NULL, *partend;
|
||||
|
||||
/* everything, including the other helper functions, works on the
|
||||
* same buffer area 'namebuffer'. The pointers blockend, diskend
|
||||
* and partend point to the end of the various names.
|
||||
* Example:
|
||||
* "/sys/block/dasda/dasda1/"
|
||||
* ^ blockend
|
||||
* ^ diskend
|
||||
* ^ partend
|
||||
*/
|
||||
|
||||
strcpy(namebuffer,BLOCKPATH);
|
||||
blockdir = opendir(namebuffer);
|
||||
if (!blockdir)
|
||||
return -1;
|
||||
blockend = namebuffer + strlen(namebuffer);
|
||||
/* check each entry in /sys/block */
|
||||
while ((blockde = readdir(blockdir))) {
|
||||
diskend = append_if_directory(namebuffer, blockend, blockde);
|
||||
if (!diskend)
|
||||
continue;
|
||||
found = check_directory(namebuffer, dev);
|
||||
if (found)
|
||||
break;
|
||||
diskdir = opendir(namebuffer);
|
||||
if (!diskdir)
|
||||
continue;
|
||||
/* check each entry in /sys/block/<disk name> */
|
||||
while ((diskde = readdir(diskdir))) {
|
||||
partend = append_if_directory(
|
||||
namebuffer, diskend, diskde);
|
||||
if (!partend)
|
||||
continue;
|
||||
found = check_directory(namebuffer, dev);
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
closedir(diskdir);
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
closedir(blockdir);
|
||||
if (found) {
|
||||
*diskend = 0; /* remove partition directory from name */
|
||||
return extract_busid(namebuffer, busid);
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function that searches for a specific block device in
|
||||
* /proc/dasd/devices and returns it's bus-ID
|
||||
* maja, mina: <major>, <minor> of the device
|
||||
* busid: buffer in which the bus-ID string will be returned
|
||||
* returns 0 for successful operation and -1 in case of an error
|
||||
* e.g. /proc/dasd/devices does not exist.
|
||||
*
|
||||
* An entry looks like:
|
||||
* 0.0.XXXX(DISCIPLINE) at ( MAJ: MIN) is dasdX :
|
||||
* active at blocksize: BLOCKSIZE, BLOCKS blocks, SIZE MB
|
||||
*/
|
||||
static int find_busid_in_proc(int maja, int mina, char *busid)
|
||||
{
|
||||
FILE *filp;
|
||||
char bus[BUSIDSIZE];
|
||||
int majb, minb, rc;
|
||||
size_t len;
|
||||
|
||||
rc = -1;
|
||||
|
||||
filp = fopen("/proc/dasd/devices", "r");
|
||||
if (!filp)
|
||||
return rc;
|
||||
while (fscanf(filp, "%[^(] %*[^)] ) at ( %d : %d %*[^\n]\n",
|
||||
bus, &majb, &minb) != EOF) {
|
||||
if ((maja == majb) && (mina == minb)) {
|
||||
len = util_strlcpy(busid, bus, BUSIDSIZE);
|
||||
if (len < BUSIDSIZE)
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(filp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the busid of a given device node.
|
||||
* Works only for block devices.
|
||||
* devicenode: path to the device node
|
||||
* busid: buffer in which the busid string will be returned
|
||||
* returns 0 for successful operation and -1 in case of an error.
|
||||
*/
|
||||
int u2s_getbusid(char *devicenode, char *busid)
|
||||
{
|
||||
int maj, min, rc;
|
||||
struct stat stat_buf;
|
||||
char dev_string[DEV_BUFFER_LENGTH];
|
||||
|
||||
/*
|
||||
* Get major and minor information of the device special file
|
||||
* and combine them to a <maj>:<min> string, as returned by
|
||||
* the dev attributes in sysfs
|
||||
*/
|
||||
if (stat(devicenode, &stat_buf))
|
||||
return -1;
|
||||
if (!S_ISBLK(stat_buf.st_mode))
|
||||
return -1;
|
||||
maj = major(stat_buf.st_rdev);
|
||||
min = minor(stat_buf.st_rdev);
|
||||
|
||||
rc = find_busid_in_proc(maj, min, busid);
|
||||
if (rc) {
|
||||
snprintf(dev_string, DEV_BUFFER_LENGTH, "%u:%u", maj, min);
|
||||
rc = find_busid_in_sysfs(dev_string, busid);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to find the sysfs entry for the given busid and reads
|
||||
* the contents of a specified attribute to the buffer
|
||||
*/
|
||||
int u2s_read_attribute(char *busid, char *attribute, char *buffer,
|
||||
size_t count)
|
||||
{
|
||||
char path[100];
|
||||
int rc, fd;
|
||||
ssize_t rcount;
|
||||
|
||||
rc = 0;
|
||||
snprintf(path, sizeof(path), "/sys/bus/ccw/devices/%s/%s",
|
||||
busid, attribute);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return errno;
|
||||
rcount = read(fd, buffer, count);
|
||||
if (rcount < 0)
|
||||
rc = errno;
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int u2s_get_host_access_count(char *devicenode)
|
||||
{
|
||||
char busid[BUSIDSIZE];
|
||||
unsigned long value;
|
||||
char buffer[10];
|
||||
char *endp;
|
||||
|
||||
u2s_getbusid(devicenode, busid);
|
||||
u2s_read_attribute(busid, "host_access_count", buffer, sizeof(buffer));
|
||||
|
||||
value = strtoul(buffer, &endp, 0);
|
||||
|
||||
if (endp == buffer)
|
||||
return -EINVAL;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -27,7 +27,8 @@ objects = util_base.o \
|
||||
util_part.o \
|
||||
util_prg.o \
|
||||
util_proc.o \
|
||||
util_rec.o
|
||||
util_rec.o \
|
||||
util_sys.o
|
||||
|
||||
util_base_example: util_base_example.o $(lib)
|
||||
util_panic_example: util_panic_example.o $(lib)
|
||||
|
||||
@@ -64,6 +64,7 @@ struct rec_fmt {
|
||||
int argz_sep;
|
||||
} csv_p;
|
||||
} d;
|
||||
int indent;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -122,9 +123,50 @@ struct util_rec *util_rec_new_wide(const char *hdr_sep)
|
||||
rec->fmt.type = REC_FMT_WIDE;
|
||||
rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep);
|
||||
rec->fmt.d.wide_p.argz_sep = ',';
|
||||
rec->fmt.indent = 0;
|
||||
return rec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the indentation characters
|
||||
*/
|
||||
static inline void rec_print_indention(int indent)
|
||||
{
|
||||
if (indent <= 0)
|
||||
return;
|
||||
|
||||
printf("%*s", indent, "");
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record separator in "wide" output format
|
||||
*/
|
||||
static void rec_print_wide_separator(struct util_rec *rec)
|
||||
{
|
||||
const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep;
|
||||
int size = 0, field_count = 0;
|
||||
struct util_rec_fld *fld;
|
||||
char *buf;
|
||||
|
||||
if (!hdr_sep)
|
||||
return;
|
||||
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (fld->hdr) {
|
||||
size += fld->width;
|
||||
field_count++;
|
||||
}
|
||||
}
|
||||
|
||||
size += field_count - 1;
|
||||
buf = util_malloc(size + 1);
|
||||
memset(buf, (int)hdr_sep[0], size);
|
||||
buf[size] = 0;
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
printf("%s\n", buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print record header in "wide" output format
|
||||
*/
|
||||
@@ -135,6 +177,7 @@ static void rec_print_wide_hdr(struct util_rec *rec)
|
||||
struct util_rec_fld *fld;
|
||||
char *buf;
|
||||
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (col_nr)
|
||||
printf(" ");
|
||||
@@ -156,6 +199,7 @@ static void rec_print_wide_hdr(struct util_rec *rec)
|
||||
buf = util_malloc(size + 1);
|
||||
memset(buf, (int)hdr_sep[0], size);
|
||||
buf[size] = 0;
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
printf("%s\n", buf);
|
||||
free(buf);
|
||||
}
|
||||
@@ -172,6 +216,7 @@ void rec_print_wide(struct util_rec *rec)
|
||||
int fld_count = 0;
|
||||
char *entry;
|
||||
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (!fld->hdr)
|
||||
continue;
|
||||
@@ -225,6 +270,7 @@ struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep,
|
||||
rec->fmt.d.long_p.key_size = key_size;
|
||||
rec->fmt.d.long_p.val_size = val_size;
|
||||
rec->fmt.d.long_p.argz_sep = ' ';
|
||||
rec->fmt.indent = 0;
|
||||
return rec;
|
||||
}
|
||||
|
||||
@@ -241,6 +287,7 @@ static void rec_print_long_hdr(struct util_rec *rec)
|
||||
fld = rec_get_fld(rec, p->key);
|
||||
util_assert(fld != NULL, "Record not found\n");
|
||||
util_assert(fld->hdr != NULL, "Header for field not found\n");
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
if (p->col_sep) {
|
||||
printf("%-*s %s %-*s\n", p->key_size, fld->hdr,
|
||||
p->col_sep, fld->width, fld->val);
|
||||
@@ -255,6 +302,7 @@ static void rec_print_long_hdr(struct util_rec *rec)
|
||||
buf = util_malloc(len + 1);
|
||||
memset(buf, p->hdr_sep[0], len);
|
||||
buf[len] = 0;
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
printf("%s\n", buf);
|
||||
free(buf);
|
||||
}
|
||||
@@ -277,19 +325,24 @@ static void rec_print_long(struct util_rec *rec)
|
||||
continue;
|
||||
if (!fld->val)
|
||||
continue;
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
item = argz_next(fld->val, fld->len, item);
|
||||
if (p->col_sep) {
|
||||
printf(" %-*s %s %s\n",
|
||||
p->key_size - 8, fld->hdr, p->col_sep, item);
|
||||
while ((item = argz_next(fld->val, fld->len, item)))
|
||||
while ((item = argz_next(fld->val, fld->len, item))) {
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
printf(" %-*s %c %s\n",
|
||||
p->key_size - 8, "", p->argz_sep, item);
|
||||
}
|
||||
} else {
|
||||
printf(" %-*s %s\n",
|
||||
p->key_size - 8, fld->hdr, fld->val);
|
||||
while ((item = argz_next(fld->val, fld->len, item)))
|
||||
while ((item = argz_next(fld->val, fld->len, item))) {
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
printf(" %-*s %s\n",
|
||||
p->key_size - 8, "", item);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@@ -320,6 +373,7 @@ struct util_rec *util_rec_new_csv(const char *col_sep)
|
||||
rec->fmt.type = REC_FMT_CSV;
|
||||
rec->fmt.d.csv_p.col_sep = util_strdup(col_sep);
|
||||
rec->fmt.d.csv_p.argz_sep = ' ';
|
||||
rec->fmt.indent = 0;
|
||||
return rec;
|
||||
}
|
||||
|
||||
@@ -332,6 +386,7 @@ void rec_print_csv_hdr(struct util_rec *rec)
|
||||
struct util_rec_fld *fld;
|
||||
int fld_count = 0;
|
||||
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
util_list_iterate(rec->list, fld) {
|
||||
if (fld_count)
|
||||
printf("%c", *col_sep);
|
||||
@@ -354,6 +409,7 @@ void rec_print_csv(struct util_rec *rec)
|
||||
int fld_count = 0;
|
||||
char *item = NULL;
|
||||
|
||||
rec_print_indention(rec->fmt.indent);
|
||||
util_list_iterate(rec->list, fld) {
|
||||
item = argz_next(fld->val, fld->len, item);
|
||||
if (fld_count)
|
||||
@@ -470,6 +526,24 @@ void util_rec_print_hdr(struct util_rec *rec)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print record separator according to output format
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
*/
|
||||
void util_rec_print_separator(struct util_rec *rec)
|
||||
{
|
||||
switch (rec->fmt.type) {
|
||||
case REC_FMT_WIDE:
|
||||
rec_print_wide_separator(rec);
|
||||
break;
|
||||
case REC_FMT_LONG:
|
||||
break;
|
||||
case REC_FMT_CSV:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a field value to an argz vector
|
||||
*
|
||||
@@ -537,3 +611,14 @@ const char *util_rec_get(struct util_rec *rec, const char *key)
|
||||
|
||||
return (fld != NULL) ? fld->val : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indentation of the record
|
||||
*
|
||||
* @param[in] rec Record pointer
|
||||
* @param[in] indent Number of characters to indent
|
||||
*/
|
||||
void util_rec_set_indent(struct util_rec *rec, int indent)
|
||||
{
|
||||
rec->fmt.indent = indent;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ static void print_records(const char *format, struct util_rec *rec)
|
||||
/* Print the record */
|
||||
util_rec_print(rec);
|
||||
}
|
||||
/* Print a separator line (is a nop for long and csv format) */
|
||||
util_rec_print_separator(rec);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -72,6 +74,11 @@ int main(void)
|
||||
print_records("Wide format", rec);
|
||||
util_rec_free(rec);
|
||||
|
||||
rec = util_rec_new_wide("-");
|
||||
util_rec_set_indent(rec, 4);
|
||||
print_records("Wide format with indentation", rec);
|
||||
util_rec_free(rec);
|
||||
|
||||
rec = util_rec_new_long("-", ":", "number", 30, 20);
|
||||
print_records("Long format", rec);
|
||||
util_rec_free(rec);
|
||||
|
||||
77
libutil/util_sys.c
Normal file
77
libutil/util_sys.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* SysFS helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2019
|
||||
*
|
||||
* 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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_sys.h"
|
||||
|
||||
/* lstat() doesn't work for sysfs files, a fixed size is therefore inevitable */
|
||||
#define READLINK_SIZE 256
|
||||
|
||||
/**
|
||||
* Identify device address
|
||||
*
|
||||
* Identifying the device address with this function works for almost any
|
||||
* character and block device (e.g. NVMe, SCSI, DASD, etc).
|
||||
* The user must provide a buffer that is large enough for the desired device
|
||||
* address to be read into \p addr.
|
||||
*
|
||||
* @param[in] dev Device node of interest
|
||||
* @param[out] addr Identified device address
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -1 Error while reading device information or
|
||||
* constructed path
|
||||
*/
|
||||
int util_sys_get_dev_addr(const char *dev, char *addr)
|
||||
{
|
||||
char device[READLINK_SIZE], *result;
|
||||
unsigned int maj, min;
|
||||
struct stat s;
|
||||
ssize_t len;
|
||||
char *path;
|
||||
|
||||
if (stat(dev, &s) != 0)
|
||||
return -1;
|
||||
|
||||
maj = major(s.st_rdev);
|
||||
min = minor(s.st_rdev);
|
||||
|
||||
if (S_ISBLK(s.st_mode))
|
||||
path = util_path_sysfs("dev/block/%u:%u/device", maj, min);
|
||||
else if (S_ISCHR(s.st_mode))
|
||||
path = util_path_sysfs("dev/char/%u:%u/device", maj, min);
|
||||
else
|
||||
return -1;
|
||||
|
||||
len = readlink(path, device, READLINK_SIZE - 1);
|
||||
free(path);
|
||||
if (len != -1)
|
||||
device[len] = '\0';
|
||||
else
|
||||
return -1;
|
||||
|
||||
result = strrchr(device, '/');
|
||||
if (result)
|
||||
result++;
|
||||
else
|
||||
result = device;
|
||||
strcpy(addr, result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
188
libzds/libzds.c
188
libzds/libzds.c
@@ -20,10 +20,8 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/dasd_base.h"
|
||||
#include "lib/dasd_sys.h"
|
||||
#include "lib/libzds.h"
|
||||
#include "lib/u2s.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/vtoc.h"
|
||||
|
||||
/** @cond PRIVATE */
|
||||
@@ -70,42 +68,6 @@ struct errormsg {
|
||||
char text[ERRORMSG];
|
||||
};
|
||||
|
||||
/**
|
||||
* As the VTOC is the data area on the DASD that describes all data sets,
|
||||
* this library will often have to refer to the various records in the VTOC.
|
||||
* To make this more efficiant, we will read the whole VTOC once and identify
|
||||
* all elements (DSCBs). The raw data of the VTOC tracks and the index to the
|
||||
* DSCBs is stored.
|
||||
*/
|
||||
struct raw_vtoc {
|
||||
/** @brief The raw track data */
|
||||
char *rawdata;
|
||||
/** @brief This size of the raw track data in bytes */
|
||||
unsigned long long rawdatasize;
|
||||
/** @brief An array with pointers to the various DSCBs in the rawdata */
|
||||
char **vtocindex;
|
||||
/** @brief Number of entries in the index */
|
||||
unsigned int vtocindexcount;
|
||||
/** @brief Number of records per VTOC track
|
||||
*
|
||||
* @note While the DS4DEVDT field in the format 4 DSCB names the number
|
||||
* if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1
|
||||
* for record 0.
|
||||
*/
|
||||
unsigned int vtoc_rec_per_track;
|
||||
/** @brief The track number in which the vtoc begins on the DASD */
|
||||
unsigned int vtoctrackoffset;
|
||||
/** @brief Start record of VTOC.
|
||||
*
|
||||
* The rawdata contains full tracks. This is the number of the first
|
||||
* record that actually belongs to the VTOC */
|
||||
unsigned int vtocrecno;
|
||||
/** @brief The DASD this vtoc was read from */
|
||||
struct dasd *dasd;
|
||||
/** @brief Detailed error messages in case of a problem */
|
||||
struct errorlog *log;
|
||||
};
|
||||
|
||||
struct dscbiterator {
|
||||
/** @brief The raw_vtoc this iterator refers to */
|
||||
struct raw_vtoc *rawvtoc;
|
||||
@@ -113,31 +75,6 @@ struct dscbiterator {
|
||||
unsigned int i;
|
||||
};
|
||||
|
||||
struct dasd {
|
||||
/** @brief List head used to store a list of DASDs in struct zdsroot */
|
||||
struct util_list_node list;
|
||||
/** @brief Name of the block device, e.g. /dev/dasde */
|
||||
char *device;
|
||||
/** @brief File descriptor for the block device.
|
||||
*
|
||||
* The device is kept open for as along as the library uses it.
|
||||
* This lets the system know that the device is still in use.
|
||||
*/
|
||||
int inusefd;
|
||||
/* @brief where to find the volume label */
|
||||
unsigned int label_block;
|
||||
/** @brief Device geometry. How many cylinders does the DASD have. */
|
||||
unsigned int cylinders;
|
||||
/** @brief Device geometry. How many heads does the DASD have. */
|
||||
unsigned int heads;
|
||||
/** @brief The VTOC data that has been read from this device */
|
||||
struct raw_vtoc *rawvtoc;
|
||||
/** @brief The volume label that has been read from this device */
|
||||
volume_label_t *vlabel;
|
||||
/** @brief Detailed error messages in case of a problem */
|
||||
struct errorlog *log;
|
||||
};
|
||||
|
||||
struct dasdhandle {
|
||||
/** @brief The struct dasd this context relates to */
|
||||
struct dasd *dasd;
|
||||
@@ -412,6 +349,29 @@ int lzds_zdsroot_alloc(struct zdsroot **root)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* It should be noted that this frees all structures that are owned by the
|
||||
* root structure as well. For example, a pointer to a struct dasd that
|
||||
* has been returned by lzds_zdsroot_add_device is not valid anymore.
|
||||
*
|
||||
* @param[in] root Reference to the zdsroot structure that is to be freed.
|
||||
*/
|
||||
void lzds_dslist_free(struct zdsroot *root)
|
||||
{
|
||||
struct dataset *ds, *nextds;
|
||||
int i;
|
||||
|
||||
util_list_iterate_safe(root->datasetlist, ds, nextds) {
|
||||
util_list_remove(root->datasetlist, ds);
|
||||
dataset_free_memberlist(ds);
|
||||
for (i = 0; i < MAXVOLUMESPERDS; ++i)
|
||||
free(ds->dsp[i]);
|
||||
errorlog_free(ds->log);
|
||||
free(ds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It should be noted that this frees all structures that are owned by the
|
||||
* root structure as well. For example, a pointer to a struct dasd that
|
||||
@@ -422,8 +382,6 @@ int lzds_zdsroot_alloc(struct zdsroot **root)
|
||||
void lzds_zdsroot_free(struct zdsroot *root)
|
||||
{
|
||||
struct dasd *dasd, *nextdasd;
|
||||
struct dataset *ds, *nextds;
|
||||
int i;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
@@ -433,15 +391,7 @@ void lzds_zdsroot_free(struct zdsroot *root)
|
||||
dasd_free(dasd);
|
||||
}
|
||||
util_list_free(root->dasdlist);
|
||||
|
||||
util_list_iterate_safe(root->datasetlist, ds, nextds) {
|
||||
util_list_remove(root->datasetlist, ds);
|
||||
dataset_free_memberlist(ds);
|
||||
for (i = 0; i < MAXVOLUMESPERDS; ++i)
|
||||
free(ds->dsp[i]);
|
||||
errorlog_free(ds->log);
|
||||
free(ds);
|
||||
}
|
||||
lzds_dslist_free(root);
|
||||
util_list_free(root->datasetlist);
|
||||
errorlog_free(root->log);
|
||||
free(root);
|
||||
@@ -1443,7 +1393,7 @@ int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p,
|
||||
* - EPROTO The VTOC data is not in a valid format.
|
||||
* - EIO Other I/O error
|
||||
*/
|
||||
int lzds_dasd_read_rawvtoc(struct dasd *dasd)
|
||||
int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc)
|
||||
{
|
||||
unsigned long long vtoctrckno, vtocrecno;
|
||||
unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize;
|
||||
@@ -1455,26 +1405,11 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd)
|
||||
format4_label_t *f4;
|
||||
unsigned long long rawvtocsize;
|
||||
|
||||
struct raw_vtoc *rawvtoc = NULL;
|
||||
volume_label_t *vlabel = NULL;
|
||||
char *trackdata = NULL;
|
||||
char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00}; /* "VOL1" in EBCDIC */
|
||||
|
||||
errorlog_clear(dasd->log);
|
||||
/* cleanup the old rawvtoc structures before we read new ones */
|
||||
rawvtoc = dasd->rawvtoc;
|
||||
dasd->rawvtoc = NULL;
|
||||
if (rawvtoc) {
|
||||
free(rawvtoc->rawdata);
|
||||
free(rawvtoc->vtocindex);
|
||||
free(rawvtoc);
|
||||
}
|
||||
|
||||
rawvtoc = malloc(sizeof(*rawvtoc));
|
||||
if (!rawvtoc)
|
||||
return ENOMEM;
|
||||
memset(rawvtoc, 0, sizeof(*rawvtoc));
|
||||
rawvtoc->dasd = dasd;
|
||||
|
||||
rc = lzds_dasd_get_vlabel(dasd, &vlabel);
|
||||
if (rc) {
|
||||
@@ -1611,13 +1546,49 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd)
|
||||
++i;
|
||||
}
|
||||
|
||||
dasd->rawvtoc = rawvtoc;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
free(rawvtoc->vtocindex);
|
||||
free(trackdata);
|
||||
free(rawvtoc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param[in] dasd The struct dasd that represents the device we want to read
|
||||
* the VTOC from.
|
||||
* @return 0 on success, otherwise one of the following error codes:
|
||||
* - ENOMEM Could not allocate internal structure due to lack of memory.
|
||||
* - EINVAL The volume label has not yet been read or it is not valid.
|
||||
* - EPROTO The VTOC data is not in a valid format.
|
||||
* - EIO Other I/O error
|
||||
*/
|
||||
int lzds_dasd_alloc_rawvtoc(struct dasd *dasd)
|
||||
{
|
||||
struct raw_vtoc *rawvtoc = NULL;
|
||||
int rc;
|
||||
|
||||
/* cleanup the old rawvtoc structures before we read new ones */
|
||||
rawvtoc = dasd->rawvtoc;
|
||||
dasd->rawvtoc = NULL;
|
||||
if (rawvtoc) {
|
||||
free(rawvtoc->rawdata);
|
||||
free(rawvtoc->vtocindex);
|
||||
free(rawvtoc);
|
||||
}
|
||||
|
||||
rawvtoc = malloc(sizeof(*rawvtoc));
|
||||
if (!rawvtoc)
|
||||
return ENOMEM;
|
||||
memset(rawvtoc, 0, sizeof(*rawvtoc));
|
||||
rawvtoc->dasd = dasd;
|
||||
|
||||
rc = lzds_dasd_read_rawvtoc(dasd, rawvtoc);
|
||||
if (rc) {
|
||||
free(rawvtoc->vtocindex);
|
||||
free(rawvtoc);
|
||||
} else {
|
||||
dasd->rawvtoc = rawvtoc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2261,6 +2232,7 @@ out1:
|
||||
static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds)
|
||||
{
|
||||
int k, l, dspcount;
|
||||
|
||||
for (k = 0; k < MAXVOLUMESPERDS; ++k) {
|
||||
/* if both datasets have a part in position k,
|
||||
* then something is wrong */
|
||||
@@ -2280,18 +2252,20 @@ static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds)
|
||||
* Since dsp[0] may not be set yet, we loop over the
|
||||
* base dsp array until we find an entry.
|
||||
*/
|
||||
for (l = 0; l < MAXVOLUMESPERDS; ++l)
|
||||
if (baseds->dsp[l]) {
|
||||
if (memcmp(baseds->dsp[l]->f1->DS1DSSN,
|
||||
newds->dsp[k]->f1->DS1DSSN,
|
||||
MAXVOLSER))
|
||||
return errorlog_add_message(
|
||||
&baseds->log, NULL, EPROTO,
|
||||
"merge dataset: part %d has incompatible"
|
||||
" base volume serial\n", k);
|
||||
else
|
||||
break;
|
||||
}
|
||||
for (l = 0; l < MAXVOLUMESPERDS; ++l) {
|
||||
if (!baseds->dsp[l])
|
||||
continue;
|
||||
if (memcmp(baseds->dsp[l]->f1->DS1DSSN,
|
||||
newds->dsp[k]->f1->DS1DSSN,
|
||||
MAXVOLSER))
|
||||
return errorlog_add_message(
|
||||
&baseds->log, NULL, EPROTO,
|
||||
"merge dataset: part %d has incompatible base volume serial\n",
|
||||
k);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
baseds->dsp[k] = newds->dsp[k];
|
||||
baseds->dspcount++;
|
||||
|
||||
@@ -3727,7 +3701,7 @@ int lzds_analyse_open_count(struct zdsroot *root, int warn)
|
||||
int rc = 0;
|
||||
|
||||
util_list_iterate(root->dasdlist, dasd) {
|
||||
value = u2s_get_host_access_count(dasd->device);
|
||||
value = dasd_get_host_access_count(dasd->device);
|
||||
|
||||
if (value < 0) {
|
||||
fprintf(stderr,
|
||||
|
||||
@@ -32,7 +32,7 @@ static char small_mon_record[SMALL_MON_RECORD_LEN];
|
||||
static char large_mon_record[LARGE_MON_RECORD_LEN];
|
||||
static long sample_interval = 60;
|
||||
|
||||
static const char *pid_file = "/var/run/mon_fsstatd.pid";
|
||||
static const char *pid_file = "/run/mon_fsstatd.pid";
|
||||
|
||||
struct mw_name_lens {
|
||||
__u16 mw_name_len;
|
||||
|
||||
@@ -59,7 +59,7 @@ static char buf[BUF_SIZE];
|
||||
static char mon_record[MAX_REC_LEN];
|
||||
static long sample_interval = 60;
|
||||
|
||||
static const char *pid_file = "/var/run/mon_procd.pid";
|
||||
static const char *pid_file = "/run/mon_procd.pid";
|
||||
|
||||
/*
|
||||
* Clean up when SIGTERM or SIGINT received
|
||||
|
||||
@@ -167,6 +167,9 @@ readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out"
|
||||
# File that includes content of files from sysfs
|
||||
readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfsfiles.out"
|
||||
|
||||
# File that includes the output of lsof
|
||||
readonly OUTPUT_FILE_LSOF="${WORKPATH}open_files.out"
|
||||
|
||||
# File that includes content of OSA OAT
|
||||
readonly OUTPUT_FILE_OSAOAT="${WORKPATH}osa_oat"
|
||||
|
||||
@@ -307,12 +310,13 @@ LOGFILES="\
|
||||
/var/log/lin_tape.trace\
|
||||
/var/log/lin_tape.errorlog\
|
||||
/var/log/messages*\
|
||||
/var/log/opencryptoki\
|
||||
/var/log/syslog*\
|
||||
/var/log/sa\
|
||||
/var/log/yum.log\
|
||||
/var/log/openvswitch/ovs-vswitchd.log\
|
||||
/var/log/openvswitch/ovsdb-server.log\
|
||||
/var/run/docker/libcontainerd/containerd/events.log\
|
||||
/run/docker/libcontainerd/containerd/events.log\
|
||||
/run/containerd/events.log\
|
||||
"
|
||||
|
||||
@@ -441,7 +445,8 @@ CMDS="uname -a\
|
||||
:SPident\
|
||||
:rpm -qa | sort\
|
||||
:sysctl -a\
|
||||
:lsof\
|
||||
:lsof \
|
||||
> '${OUTPUT_FILE_LSOF}'\
|
||||
:mount\
|
||||
:df -h\
|
||||
:df -i\
|
||||
@@ -465,6 +470,8 @@ CMDS="uname -a\
|
||||
:docker version\
|
||||
:docker stats --no-stream\
|
||||
:systemctl status docker.service\
|
||||
:blockdev --report\
|
||||
:lvdisplay\
|
||||
"
|
||||
|
||||
########################################
|
||||
|
||||
@@ -291,6 +291,13 @@ sub get_payload_records
|
||||
my $area = str_from_hex(substr($record[2], 2, 14));
|
||||
my $counter = hex(substr($record[2], 0, 2));
|
||||
my $fsf_req_id = substr($record[2], 16, 16);
|
||||
# ($counter == 0) is just a simple heuristic which can fail if
|
||||
# there are missing payload record junks due to PAY area wrap!
|
||||
if ($counter == 0 &&
|
||||
defined($PAYLOAD_RECORDS{$fsf_req_id}{$area})) {
|
||||
print "Warning: Ambiguous PAYload records. reqid:" .
|
||||
$fsf_req_id . " area:" . $area . "\n";
|
||||
}
|
||||
$PAYLOAD_RECORDS{$fsf_req_id}{$area}[$counter] = [@record];
|
||||
if ($def_error && ($area =~ /def_err/)) {
|
||||
$def_error{$fsf_req_id}[$counter] = [@record];
|
||||
@@ -515,11 +522,16 @@ sub print_payload
|
||||
my $field_name = shift();
|
||||
my $tmp_str;
|
||||
|
||||
if ($payload) {
|
||||
# workaround to let user determine ambiguous PAYload records
|
||||
printf "%-14s : %s\n", "Payload time", $payload->[0]->[0];
|
||||
}
|
||||
printf "%-14s : ", $field_name;
|
||||
if (!$payload) {
|
||||
print "record not available anymore.\n";
|
||||
return;
|
||||
}
|
||||
# TODO: iterate with $counter loop and notify user about missing junks
|
||||
foreach my $cc (@$payload) {
|
||||
$tmp_str .= substr($cc->[2], 32);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ ConditionVirtualization=no
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStart=@usrsbin_path@/cpacfstatsd
|
||||
PIDFile=/var/run/cpacfstatsd.pid
|
||||
PIDFile=/run/cpacfstatsd.pid
|
||||
KillMode=process
|
||||
Restart=no
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ ExecStart=@usrsbin_path@/cpuplugd -c @sysconf_path@/cpuplugd.conf
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
KillMode=process
|
||||
Type=forking
|
||||
PIDFile=/var/run/cpuplugd.pid
|
||||
PIDFile=/run/cpuplugd.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -34,7 +34,7 @@ ExecStart=@usrsbin_path@/mon_fsstatd -i $FSSTAT_INTERVAL
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
KillMode=process
|
||||
Type=forking
|
||||
PIDFile=/var/run/mon_fsstatd.pid
|
||||
PIDFile=/run/mon_fsstatd.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -34,7 +34,7 @@ ExecStart=@usrsbin_path@/mon_procd -i $PROC_INTERVAL
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
KillMode=process
|
||||
Type=forking
|
||||
PIDFile=/var/run/mon_procd.pid
|
||||
PIDFile=/run/mon_procd.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -3,7 +3,6 @@ include ../../common.mak
|
||||
ALL_CPPFLAGS += -I../include -I../boot
|
||||
|
||||
libs = $(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
all: tunedasd
|
||||
|
||||
@@ -11,10 +11,24 @@ lschp \- list information about available channel\-paths.
|
||||
.B lschp
|
||||
.RB [ \-h|\-\-help ]
|
||||
.RB [ \-v|\-\-version ]
|
||||
.RB [
|
||||
.I CHPID
|
||||
]
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lschp command lists status and type information about available
|
||||
channel\-paths.
|
||||
channel\-paths. A
|
||||
.I CHPID
|
||||
argument can be specified to return the information for the respective
|
||||
channel-path identifier only.
|
||||
Channel\-path identifiers are specified in hexadecimal notation either simply
|
||||
as the CHPID\-number (e.g. e0) or in the form
|
||||
|
||||
.RS
|
||||
<cssid>.<id>
|
||||
.RE
|
||||
|
||||
where <cssid> is the channel\-subsystem identifier and <id> is the CHPID\-number (e.g. 0.7e).
|
||||
|
||||
.B Column description:
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Provide main function and command line parsing.
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
* Copyright IBM Corp. 2016, 2019
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <err.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
@@ -31,7 +32,7 @@ static const struct util_prg prg = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2016,
|
||||
.pub_last = 2017,
|
||||
.pub_last = 2019,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
@@ -55,9 +56,10 @@ static struct util_opt opt_vec[] = {
|
||||
* @param[in] dir Path of the desired directory
|
||||
* @param[in] css_id ID for device identification
|
||||
* @param[in] rec The buffer structure, where results are written to
|
||||
* @param[in] chp If set: CHPID to filter by
|
||||
*/
|
||||
static void print_chpid(const char *chp_dir, unsigned int css_id,
|
||||
struct util_rec *rec)
|
||||
struct util_rec *rec, char *chp)
|
||||
{
|
||||
unsigned int css_id_tmp, chp_id;
|
||||
bool chid_external;
|
||||
@@ -123,7 +125,8 @@ static void print_chpid(const char *chp_dir, unsigned int css_id,
|
||||
} else {
|
||||
util_rec_set(rec, "pchid", "%s", "-");
|
||||
}
|
||||
util_rec_print(rec);
|
||||
if (!strlen(chp) || strcmp(util_rec_get(rec, "chpid"), chp) == 0)
|
||||
util_rec_print(rec);
|
||||
free(path);
|
||||
}
|
||||
|
||||
@@ -153,8 +156,9 @@ static int chpsort(const struct dirent **de1, const struct dirent **de2)
|
||||
*
|
||||
* @param[in] css_dir The desired directory
|
||||
* @param[in] rec The buffer structure, where results are written to
|
||||
* @param[in] chp If set: CHPID to filter by
|
||||
*/
|
||||
static void print_css(const char *css_dir, struct util_rec *rec)
|
||||
static void print_css(const char *css_dir, struct util_rec *rec, char *chp)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
unsigned int css_id;
|
||||
@@ -167,15 +171,17 @@ static void print_css(const char *css_dir, struct util_rec *rec)
|
||||
path = util_path_sysfs("devices/css%d", css_id);
|
||||
count = util_scandir(&de_vec, chpsort, path, "chp%x.*", css_id);
|
||||
for (i = 0; i < count; i++)
|
||||
print_chpid(de_vec[i]->d_name, css_id, rec);
|
||||
print_chpid(de_vec[i]->d_name, css_id, rec, chp);
|
||||
util_scandir_free(de_vec, count);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print chpid table
|
||||
*
|
||||
* @param[in] chp If set: CHPID to filter by
|
||||
*/
|
||||
static void cmd_lschp(void)
|
||||
static void cmd_lschp(char *chp)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
struct util_rec *rec;
|
||||
@@ -198,17 +204,19 @@ static void cmd_lschp(void)
|
||||
path = util_path_sysfs("devices");
|
||||
count = util_scandir(&de_vec, alphasort, path, "^css[[:xdigit:]]{1,2}$");
|
||||
for (i = 0; i < count; i++)
|
||||
print_css(de_vec[i]->d_name, rec);
|
||||
print_css(de_vec[i]->d_name, rec, chp);
|
||||
util_ptr_vec_free((void **) de_vec, count);
|
||||
free(path);
|
||||
util_rec_free(rec);
|
||||
}
|
||||
|
||||
#define CHP_LEN 4
|
||||
/*
|
||||
* Parse options and execute the command
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char chp[CHP_LEN + 1] = "";
|
||||
int c;
|
||||
|
||||
util_prg_init(&prg);
|
||||
@@ -231,10 +239,30 @@ int main(int argc, char *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (argc > optind) {
|
||||
util_prg_print_arg_error(argv[optind]);
|
||||
return EXIT_FAILURE;
|
||||
if (argc > optind + 1)
|
||||
errx(EXIT_FAILURE, "Too many arguments specified");
|
||||
if (argc == optind + 1) {
|
||||
/* we take a single argument only */
|
||||
switch (strlen(argv[optind])) {
|
||||
case 1:
|
||||
sprintf(chp, "0.0%s", argv[optind]);
|
||||
break;
|
||||
case 2:
|
||||
sprintf(chp, "0.%s", argv[optind]);
|
||||
break;
|
||||
case CHP_LEN:
|
||||
strcpy(chp, argv[optind]);
|
||||
break;
|
||||
default:
|
||||
errx(EXIT_FAILURE, "%s is not a valid channel-path ID",
|
||||
argv[optind]);
|
||||
}
|
||||
if (!isdigit(chp[0]) || chp[1] != '.' || !isxdigit(chp[2]) ||
|
||||
!isxdigit(chp[3]))
|
||||
errx(EXIT_FAILURE, "%s is not a valid channel-path ID",
|
||||
chp);
|
||||
}
|
||||
cmd_lschp();
|
||||
cmd_lschp(chp);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
357
zconf/lszfcp
357
zconf/lszfcp
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# lszfcp - Tool to display information about zfcp devices (adapters/ports/units)
|
||||
#
|
||||
# Copyright IBM Corp. 2006, 2017
|
||||
# Copyright IBM Corp. 2006, 2019
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -14,10 +14,13 @@ FC_CLASS=false
|
||||
|
||||
# Command line parameters
|
||||
VERBOSITY=0
|
||||
SHOW_MODPARMS=0
|
||||
SHOW_HOSTS=0
|
||||
SHOW_PORTS=0
|
||||
SHOW_DEVICES=0
|
||||
SHOW_ATTRIBUTES=false
|
||||
SHOW_MORE_ATTRS=0
|
||||
SHOW_EXTENDED=false
|
||||
unset PAR_BUSID PAR_WWPN PAR_LUN
|
||||
|
||||
|
||||
@@ -26,8 +29,8 @@ unset PAR_BUSID PAR_WWPN PAR_LUN
|
||||
check_sysfs()
|
||||
{
|
||||
if [ -z $SYSFS -o ! -d $SYSFS -o ! -r $SYSFS ]; then
|
||||
echo "Error: sysfs not available."
|
||||
exit 1
|
||||
echo "Error: sysfs not available."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -43,13 +46,36 @@ check_zfcp_support()
|
||||
|
||||
check_fcp_devs()
|
||||
{
|
||||
local found=0
|
||||
|
||||
if $FC_CLASS; then
|
||||
# theoretically this includes "defunct" (and thus online) devs
|
||||
ignore=`ls $SYSFS/class/fc_host/host* 2>&1`
|
||||
else
|
||||
ignore=`ls $SYSFS/devices/css0/[0-9]*/[0-9]*/host[0-9]* 2>&1`
|
||||
fi
|
||||
[ $? -eq 0 ] && found=1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ $found -eq 0 ] && $SHOW_EXTENDED; then
|
||||
# nothing found yet so search for "defunct" FCP devices
|
||||
ignore=$(ls -dX $SYSFS/devices/css[0-9]*/defunct/[0-9]*/host[0-9]* 2>&1)
|
||||
[ $? -eq 0 ] && found=1
|
||||
fi
|
||||
if [ $found -eq 0 ] && $SHOW_EXTENDED; then
|
||||
local mypath mylist=""
|
||||
# nothing found yet so search for FCP devices never been online
|
||||
for mypath in $SYSFS/bus/ccw/drivers/zfcp/*.*.*; do
|
||||
if [ "$mypath" = "$SYSFS/bus/ccw/drivers/zfcp/*.*.*" ];
|
||||
then
|
||||
break # glob did not match anything
|
||||
fi
|
||||
[ -d $mypath/host[0-9]* ] && continue # is/was online
|
||||
mylist="$mylist $(readlink -e $mypath)/-"
|
||||
done
|
||||
[ -n "$mylist" ] && found=1
|
||||
fi
|
||||
|
||||
if [ $found -eq 0 ]; then
|
||||
echo "Error: No fcp devices found."
|
||||
exit 1
|
||||
fi
|
||||
@@ -57,7 +83,7 @@ check_fcp_devs()
|
||||
|
||||
check_fc_class()
|
||||
{
|
||||
if [ -d "$SYSFS/class/fc_host" ]; then
|
||||
if [ -d "$SYSFS/class/fc_host" ]; then
|
||||
FC_CLASS=true
|
||||
fi
|
||||
}
|
||||
@@ -66,7 +92,7 @@ print_version()
|
||||
{
|
||||
cat <<EOF
|
||||
$SCRIPTNAME: version %S390_TOOLS_VERSION%
|
||||
Copyright IBM Corp. 2006, 2017
|
||||
Copyright IBM Corp. 2006, 2019
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -84,18 +110,24 @@ OPTIONS:
|
||||
-H, --hosts show host information (default)
|
||||
-P, --ports show remote port information
|
||||
-D, --devices show SCSI device information
|
||||
-Z, --modparms show zfcp (module) parameters
|
||||
-b, --busid=BUSID select specific busid
|
||||
-p, --wwpn=WWPN select specific port name
|
||||
-l, --lun=LUN select specific LUN
|
||||
-a, --attributes show all attributes
|
||||
-a, --attributes show attributes
|
||||
-m, --moreattrs show more attributes
|
||||
(host: css; port: zfcp_port; SCSI device: zfcp_unit)
|
||||
specify twice to show more attributes for SCSI devices
|
||||
(scsi_disk,block,integrity,queue,iosched)
|
||||
-V, --verbose show sysfs paths of associated class
|
||||
and bus devices
|
||||
-e, --extended extended output format
|
||||
-s, --sysfs=PATH use path as sysfs (for dbginfo archives)
|
||||
-h, --help print this help
|
||||
-v, --version print version information
|
||||
|
||||
EXAMPLE:
|
||||
List for all zfcp adapters, ports and units the names of their
|
||||
EXAMPLE:
|
||||
List for all zfcp adapters, ports and units the names of their
|
||||
associated SCSI hosts, FC remote ports and SCSI devices.
|
||||
|
||||
#> lszfcp -P -H -D
|
||||
@@ -129,29 +161,95 @@ show_attributes()
|
||||
}
|
||||
|
||||
|
||||
show_modparms()
|
||||
{
|
||||
local modparmdir=$SYSFS/module/zfcp/parameters
|
||||
|
||||
[ -d "$modparmdir" ] || return
|
||||
|
||||
if [ $VERBOSITY -ne 0 ]; then
|
||||
echo "$modparmdir"
|
||||
fi
|
||||
|
||||
echo 'Module = "zfcp"'
|
||||
show_attributes "$modparmdir"
|
||||
echo
|
||||
}
|
||||
|
||||
|
||||
show_hosts()
|
||||
{
|
||||
HOST_LIST=`ls -dX $SYSFS/devices/css0/[0-9]*/[0-9]*/host[0-9]*`
|
||||
HOST_LIST=`ls -dX $SYSFS/devices/css0/[0-9]*/[0-9]*/host[0-9]* \
|
||||
2> /dev/null`
|
||||
|
||||
if $SHOW_EXTENDED; then
|
||||
# add all "defunct" FCP devices
|
||||
HOST_LIST="$HOST_LIST
|
||||
$(ls -dX $SYSFS/devices/css[0-9]*/defunct/[0-9]*/host[0-9]* 2>/dev/null)"
|
||||
# add all FCP devices that have never been online
|
||||
for HOST_PATH in $SYSFS/bus/ccw/drivers/zfcp/*.*.*; do
|
||||
[ -d $HOST_PATH/host[0-9]* ] && continue # is/was online
|
||||
HOST_LIST="$HOST_LIST
|
||||
$(readlink -e $HOST_PATH)/-"
|
||||
done
|
||||
fi
|
||||
|
||||
for HOST_PATH in $HOST_LIST; do
|
||||
SCSI_HOST=`basename $HOST_PATH`
|
||||
ADAPTER_PATH=`dirname $HOST_PATH`
|
||||
ADAPTER=`basename $ADAPTER_PATH`
|
||||
|
||||
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
read ONLINE < $ADAPTER_PATH/online
|
||||
read AVAILABILITY < $ADAPTER_PATH/availability
|
||||
[ -r $ADAPTER_PATH/failed ] && read FAILED < $ADAPTER_PATH/failed
|
||||
DEFUNCT=${ADAPTER_PATH%/*} # strip devbusid
|
||||
DEFUNCT=${DEFUNCT##*/} # basename
|
||||
HOSTMARKER=""
|
||||
if $SHOW_EXTENDED; then
|
||||
if [ "$DEFUNCT" = "defunct" ]; then
|
||||
HOSTMARKER=" defunct"
|
||||
elif [ "$ONLINE" = "0" ]; then
|
||||
HOSTMARKER=" offline"
|
||||
elif [ "$FAILED" = "1" ]; then
|
||||
HOSTMARKER=" failed"
|
||||
elif [ "$AVAILABILITY" != "good" ]; then
|
||||
HOSTMARKER=" NotAvailable"
|
||||
fi
|
||||
fi
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo $ADAPTER $SCSI_HOST
|
||||
echo $ADAPTER $SCSI_HOST$HOSTMARKER
|
||||
else
|
||||
echo $ADAPTER_PATH
|
||||
$FC_CLASS && echo "$SYSFS/class/fc_host/$SCSI_HOST"
|
||||
echo "$SYSFS/class/scsi_host/$SCSI_HOST"
|
||||
echo $ADAPTER_PATH$HOSTMARKER
|
||||
if [ "$SCSI_HOST" != "-" ]; then
|
||||
$FC_CLASS && echo "$SYSFS/class/fc_host/$SCSI_HOST"
|
||||
echo "$SYSFS/class/scsi_host/$SCSI_HOST"
|
||||
else
|
||||
echo "-"
|
||||
echo "-"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
if [ $SHOW_MORE_ATTRS -ge 1 ]; then
|
||||
echo 'Bus = "css"'
|
||||
if [ "$DEFUNCT" = "defunct" ]; then
|
||||
printf " %-19s\n" "defunct"
|
||||
else
|
||||
show_attributes ${ADAPTER_PATH%/*}
|
||||
fi
|
||||
fi
|
||||
|
||||
echo 'Bus = "ccw"'
|
||||
show_attributes $ADAPTER_PATH
|
||||
|
||||
if [ "$SCSI_HOST" = "-" ]; then
|
||||
# skip output of non-existent fc_host & scsi_host
|
||||
echo
|
||||
continue
|
||||
fi
|
||||
|
||||
if $FC_CLASS; then
|
||||
echo 'Class = "fc_host"'
|
||||
show_attributes \
|
||||
@@ -175,7 +273,7 @@ show_ports()
|
||||
for PORT_PATH in $PORT_LIST; do
|
||||
WWPN=`basename $PORT_PATH`
|
||||
ADAPTER=`basename \`dirname $PORT_PATH\``
|
||||
|
||||
|
||||
[ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
@@ -196,30 +294,60 @@ show_ports()
|
||||
for FC_PORT_PATH in $PORT_LIST; do
|
||||
PORT=`basename $FC_PORT_PATH`
|
||||
read PORT_STATE < $FC_PORT_PATH/port_state
|
||||
if [ "$PORT_STATE" == "Online" ];
|
||||
then
|
||||
if [ "$PORT_STATE" == "Online" ] || $SHOW_EXTENDED; then
|
||||
read WWPN < $FC_PORT_PATH/port_name
|
||||
else
|
||||
continue
|
||||
fi
|
||||
PORTSTATEMARKER=""
|
||||
$SHOW_EXTENDED && [ "$PORT_STATE" != "Online" ] \
|
||||
&& PORTSTATEMARKER=" NotOnline"
|
||||
|
||||
[ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue
|
||||
|
||||
ADAPTER_PORT_PATH=`ls -d \
|
||||
$SYSFS/devices/css0/*/*/$WWPN/../host[0-9]*/$PORT |\
|
||||
awk -F "/../host" '{ print $1 }'`
|
||||
ADAPTER=`basename \`dirname $ADAPTER_PORT_PATH\``
|
||||
local sysreal=$(readlink -e "$FC_PORT_PATH")
|
||||
local ADAPTER=""
|
||||
while [ -n "$sysreal" ]; do
|
||||
# ascend to parent: strip last path part
|
||||
sysreal=${sysreal%/*}
|
||||
[ -h $sysreal/subsystem ] || continue
|
||||
local subsystem=$(readlink -e $sysreal/subsystem)
|
||||
if [ "${subsystem##*/}" = "ccw" ]; then
|
||||
ADAPTER=${sysreal##*/}
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -z "$ADAPTER" ] && continue # skip not zfcp-attached ones
|
||||
if [ -d $SYSFS/devices/css[0-9]*/[0-9d]*/[0-9]*/$WWPN/../host[0-9]*/$PORT ];
|
||||
then
|
||||
ADAPTER_PORT_PATH=$SYSFS/devices/css[0-9]*/[0-9d]*/$ADAPTER/$WWPN
|
||||
else
|
||||
ADAPTER_PORT_PATH="-"
|
||||
fi
|
||||
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo "$ADAPTER/$WWPN $PORT"
|
||||
echo "$ADAPTER/$WWPN $PORT$PORTSTATEMARKER"
|
||||
else
|
||||
echo $ADAPTER_PORT_PATH
|
||||
echo $FC_PORT_PATH
|
||||
if [ "$ADAPTER_PORT_PATH" != "-" ]; then
|
||||
echo $ADAPTER_PORT_PATH
|
||||
else
|
||||
echo "- (NoMoreZfcpPort:$ADAPTER/$WWPN)"
|
||||
fi
|
||||
echo $FC_PORT_PATH$PORTSTATEMARKER
|
||||
fi
|
||||
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
if [ $SHOW_MORE_ATTRS -ge 1 ] \
|
||||
&& [ "$ADAPTER_PORT_PATH" != "-" ] \
|
||||
&& [ -d $ADAPTER_PORT_PATH ];
|
||||
then
|
||||
# fc_rport can exist without zfcp_port
|
||||
# (e.g. after port_remove)
|
||||
echo 'Class = "zfcp_port"'
|
||||
show_attributes "$ADAPTER_PORT_PATH"
|
||||
fi
|
||||
echo 'Class = "fc_remote_ports"'
|
||||
show_attributes "$FC_PORT_PATH"
|
||||
echo
|
||||
@@ -234,54 +362,166 @@ show_devices()
|
||||
if $FC_CLASS; then
|
||||
SCSI_DEVICE_LIST=`ls -d \
|
||||
$SYSFS/bus/ccw/drivers/zfcp/*/host*/rport*/target*/*/ \
|
||||
2>/dev/null |grep -P '\d+:\d+:\d+:\d+'`
|
||||
2>/dev/null |grep -P '\d+:\d+:\d+:\d+'`
|
||||
else
|
||||
SCSI_DEVICE_LIST=`ls -d $SYSFS/devices/css0/*/*/host[0-9]*/*/`
|
||||
fi
|
||||
|
||||
if [ -z "$SCSI_DEVICE_LIST" ]; then
|
||||
echo "Error: No fcp devices found."
|
||||
if $SHOW_EXTENDED; then
|
||||
ZFCP_UNIT_LIST=$(ls -d \
|
||||
$SYSFS/devices/css[0-9]*/[0-9d]*/[0-9]*/0x*/0x* \
|
||||
2> /dev/null)
|
||||
else
|
||||
ZFCP_UNIT_LIST=""
|
||||
fi
|
||||
|
||||
if [ -z "$SCSI_DEVICE_LIST" ] && [ -z "$ZFCP_UNIT_LIST" ]; then
|
||||
if $SHOW_EXTENDED; then
|
||||
echo "Error: No zfcp-attached SCSI devices found."
|
||||
else
|
||||
echo "Error: No fcp devices found."
|
||||
fi
|
||||
fi
|
||||
|
||||
for SCSI_DEVICE_PATH in $SCSI_DEVICE_LIST; do
|
||||
read ADAPTER < $SCSI_DEVICE_PATH/hba_id
|
||||
read WWPN < $SCSI_DEVICE_PATH/wwpn
|
||||
read LUN < $SCSI_DEVICE_PATH/fcp_lun
|
||||
|
||||
|
||||
# remove from ZFCP_UNIT_LIST if SCSI device exists
|
||||
REDUCED_LIST=""
|
||||
for UNIT_PATH in $ZFCP_UNIT_LIST; do
|
||||
STRIPPED_PATH=$UNIT_PATH
|
||||
L=${UNIT_PATH##*/}
|
||||
STRIPPED_PATH=${STRIPPED_PATH%/*}
|
||||
W=${STRIPPED_PATH##*/}
|
||||
STRIPPED_PATH=${STRIPPED_PATH%/*}
|
||||
A=${STRIPPED_PATH##*/}
|
||||
[ "$A/$W/$L" = "$ADAPTER/$WWPN/$LUN" ] && continue
|
||||
REDUCED_LIST="$REDUCED_LIST $UNIT_PATH"
|
||||
done
|
||||
ZFCP_UNIT_LIST="$REDUCED_LIST"
|
||||
|
||||
[ $LUN != ${PAR_LUN:-$LUN} ] && continue
|
||||
[ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
ZFCP_UNIT_PATH=$SYSFS/devices/css[0-9]*/[0-9d]*/$ADAPTER/$WWPN/$LUN
|
||||
SDEVMARKER=""
|
||||
if $SHOW_EXTENDED; then
|
||||
[ -d $ZFCP_UNIT_PATH ] || SDEVMARKER="$SDEVMARKER auto"
|
||||
[ -r $SCSI_DEVICE_PATH/state ] \
|
||||
&& read SDEVSTATE < $SCSI_DEVICE_PATH/state
|
||||
[ "$SDEVSTATE" != "running" ] \
|
||||
&& SDEVMARKER="$SDEVMARKER NotRunning"
|
||||
fi
|
||||
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo "$ADAPTER/$WWPN/$LUN `basename $SCSI_DEVICE_PATH`$SDEVMARKER"
|
||||
else
|
||||
echo "`ls -d $SYSFS/devices/css0/[0-9d]*/$ADAPTER`/$WWPN/$LUN"
|
||||
echo ${SCSI_DEVICE_PATH%*/}$SDEVMARKER # without trailing slash
|
||||
|
||||
# On live systems, there are links to the block and
|
||||
# generic devices. In a dbginfo archive, these links
|
||||
# are not present. Therefore, fall back to reading
|
||||
# the runtime.out log file.
|
||||
if [ `ls $SCSI_DEVICE_PATH | grep -c block:` -eq 1 ]
|
||||
then
|
||||
BLOCK_DEV=`ls $SCSI_DEVICE_PATH | grep block:`
|
||||
GEN_DEV=`ls $SCSI_DEVICE_PATH |\
|
||||
grep scsi_generic:`
|
||||
echo -n "$SYSFS/block/${BLOCK_DEV#*:} "
|
||||
echo "$SYSFS/class/scsi_generic/${GEN_DEV#*:}"
|
||||
|
||||
elif [ -d $SCSI_DEVICE_PATH/block ] && $SHOW_EXTENDED
|
||||
then
|
||||
# case without CONFIG_SYSFS_DEPRECATED
|
||||
BLOCK_DEV=$(echo $SCSI_DEVICE_PATH/block/*)
|
||||
BLOCK_DEV=${BLOCK_DEV##*/}
|
||||
echo -n "$SYSFS/block/$BLOCK_DEV "
|
||||
local SGPATH=$SCSI_DEVICE_PATH/scsi_generic
|
||||
if [ -d $SGPATH ]; then
|
||||
GEN_DEV=$(echo $SGPATH/*)
|
||||
GEN_DEV=${GEN_DEV##*/}
|
||||
echo "$SYSFS/class/scsi_generic/$GEN_DEV"
|
||||
else
|
||||
echo "-"
|
||||
fi
|
||||
|
||||
# FIXME Find a way to assign the generic devices.
|
||||
elif [ -r $SYSFS/../runtime.out ]; then
|
||||
SCSI_DEV=`basename $SCSI_DEVICE_PATH`
|
||||
echo "$SYSFS/block/"`grep -r "\[$SCSI_DEV\]"\
|
||||
$SYSFS/../runtime.out |\
|
||||
awk -F "/dev/" '{print $2}'`
|
||||
fi
|
||||
fi
|
||||
|
||||
if $SHOW_ATTRIBUTES && [ $SHOW_MORE_ATTRS -ge 1 ]; then
|
||||
# auto scan LUNs not necessarily have a zfcp_unit
|
||||
if [ -d $ZFCP_UNIT_PATH ]; then
|
||||
echo 'Class = "zfcp_unit"'
|
||||
show_attributes "$ZFCP_UNIT_PATH"
|
||||
fi
|
||||
fi
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
echo 'Class = "scsi_device"'
|
||||
show_attributes "$SCSI_DEVICE_PATH"
|
||||
fi
|
||||
if $SHOW_ATTRIBUTES && [ $SHOW_MORE_ATTRS -ge 2 ]; then
|
||||
if [ -d $SCSI_DEVICE_PATH/scsi_disk ]; then
|
||||
echo 'Class = "scsi_disk"'
|
||||
show_attributes "$SCSI_DEVICE_PATH/scsi_disk/$(basename $SCSI_DEVICE_PATH)"
|
||||
fi
|
||||
if [ -d $SCSI_DEVICE_PATH/block ]; then
|
||||
echo 'Class = "block"'
|
||||
show_attributes "$SCSI_DEVICE_PATH/block/*/"
|
||||
fi
|
||||
if [ -d $SCSI_DEVICE_PATH/block/*/integrity ]; then
|
||||
echo 'Class = "block_integrity"'
|
||||
show_attributes "$SCSI_DEVICE_PATH/block/*/integrity"
|
||||
fi
|
||||
if [ -d $SCSI_DEVICE_PATH/block/*/queue ]; then
|
||||
echo 'Class = "block_queue"'
|
||||
show_attributes "$SCSI_DEVICE_PATH/block/*/queue"
|
||||
fi
|
||||
if [ -d $SCSI_DEVICE_PATH/block/*/queue/iosched ]; then
|
||||
echo 'Class = "block_queue_iosched"'
|
||||
show_attributes "$SCSI_DEVICE_PATH/block/*/queue/iosched"
|
||||
fi
|
||||
fi
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
echo
|
||||
fi
|
||||
done
|
||||
|
||||
# what's left in ZFCP_UNIT_LIST are now units without SCSI device
|
||||
for UNIT_PATH in $ZFCP_UNIT_LIST; do
|
||||
STRIPPED_PATH=$UNIT_PATH
|
||||
LUN=${UNIT_PATH##*/}
|
||||
STRIPPED_PATH=${STRIPPED_PATH%/*}
|
||||
WWPN=${STRIPPED_PATH##*/}
|
||||
STRIPPED_PATH=${STRIPPED_PATH%/*}
|
||||
ADAPTER=${STRIPPED_PATH##*/}
|
||||
|
||||
[ $LUN != ${PAR_LUN:-$LUN} ] && continue
|
||||
[ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo "$ADAPTER/$WWPN/$LUN `basename $SCSI_DEVICE_PATH`"
|
||||
echo "$ADAPTER/$WWPN/$LUN - failed"
|
||||
else
|
||||
echo "`ls -d $SYSFS/devices/css0/*/$ADAPTER`/$WWPN/$LUN"
|
||||
echo ${SCSI_DEVICE_PATH%*/} # without trailing slash
|
||||
echo "$UNIT_PATH failed"
|
||||
echo "-"
|
||||
fi
|
||||
|
||||
# On live systems, there are links to the block and
|
||||
# generic devices. In a dbginfo archive, these links
|
||||
# are not present. Therefore, fall back to reading
|
||||
# the runtime.out log file.
|
||||
if [ `ls $SCSI_DEVICE_PATH | grep -c block:` -eq 1 ]
|
||||
then
|
||||
BLOCK_DEV=`ls $SCSI_DEVICE_PATH | grep block:`
|
||||
GEN_DEV=`ls $SCSI_DEVICE_PATH |\
|
||||
grep scsi_generic:`
|
||||
echo -n "$SYSFS/block/${BLOCK_DEV#*:} "
|
||||
echo "$SYSFS/class/scsi_generic/${GEN_DEV#*:}"
|
||||
|
||||
# FIXME Find a way to assign the generic devices.
|
||||
elif [ -r $SYSFS/../runtime.out ]; then
|
||||
SCSI_DEV=`basename $SCSI_DEVICE_PATH`
|
||||
echo "$SYSFS/block/"`grep -r "\[$SCSI_DEV\]"\
|
||||
$SYSFS/../runtime.out |\
|
||||
awk -F "/dev/" '{print $2}'`
|
||||
fi
|
||||
if $SHOW_ATTRIBUTES && [ $SHOW_MORE_ATTRS -ge 1 ]; then
|
||||
echo 'Class = "zfcp_unit"'
|
||||
show_attributes "$UNIT_PATH"
|
||||
fi
|
||||
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
echo 'Class = "scsi_device"'
|
||||
show_attributes "$SCSI_DEVICE_PATH"
|
||||
echo
|
||||
fi
|
||||
done
|
||||
@@ -290,8 +530,8 @@ show_devices()
|
||||
|
||||
##############################################################################
|
||||
|
||||
ARGS=`getopt --options ahvHPDVb:p:l:s: --longoptions \
|
||||
attributes,help,version,hosts,ports,devices,verbose,busid:,wwpn:,lun:,sysfs: \
|
||||
ARGS=`getopt --options ahvHPDVb:p:l:s:emZ --longoptions \
|
||||
attributes,help,version,hosts,ports,devices,verbose,busid:,wwpn:,lun:,sysfs:,extended,moreattrs,modparms \
|
||||
-n "$SCRIPTNAME" -- "$@"`
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
@@ -305,6 +545,8 @@ eval set -- "$ARGS"
|
||||
for ARG; do
|
||||
case "$ARG" in
|
||||
-a|--attributes) SHOW_ATTRIBUTES=true; shift 1;;
|
||||
-m|--moreattrs) ((SHOW_MORE_ATTRS++)); shift 1;;
|
||||
-e|--extended) SHOW_EXTENDED=true; shift 1;;
|
||||
-b|--busid) PAR_BUSID=$2; shift 2;;
|
||||
-h|--help) print_help; exit 0;;
|
||||
-l|--lun) PAR_LUN=$2; shift 2;;
|
||||
@@ -314,6 +556,7 @@ for ARG; do
|
||||
-D|--devices) SHOW_DEVICES=1; shift 1;;
|
||||
-P|--ports) SHOW_PORTS=1; shift 1;;
|
||||
-V|--verbose) VERBOSITY=1; shift 1;;
|
||||
-Z|--modparms) SHOW_MODPARMS=1; shift 1;;
|
||||
-s|--sysfs) SYSFS=$2; shift 2;;
|
||||
--) shift; break;;
|
||||
esac
|
||||
@@ -325,6 +568,10 @@ check_fc_class
|
||||
check_fcp_devs
|
||||
|
||||
default=1
|
||||
if [ $SHOW_MODPARMS -eq 1 ]; then
|
||||
default=0; show_modparms
|
||||
fi
|
||||
|
||||
if [ $SHOW_HOSTS -eq 1 ]; then
|
||||
default=0; show_hosts
|
||||
elif [ $SHOW_PORTS -eq 0 -a $SHOW_DEVICES -eq 0 -a -n "$PAR_BUSID" ]; then
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
.\" Copyright IBM Corp. 2006, 2017
|
||||
.\" Copyright IBM Corp. 2006, 2019
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSZFCP 8 "Mar 2008" "s390-tools"
|
||||
.TH LSZFCP 8 "Feb 2019" "s390-tools"
|
||||
.SH NAME
|
||||
lszfcp \- list information about zfcp adapters, ports, and units
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B lszfcp
|
||||
.RB [ \-hvVaHDP ]
|
||||
.RB [ \-hvVaZHDPem ]
|
||||
.RB [ \-b
|
||||
.IR busid ]
|
||||
.RB [ \-l
|
||||
@@ -28,8 +28,10 @@ scsi_devices.
|
||||
The default is to list busids of all zfcp adapters and their corresponding
|
||||
SCSI host names.
|
||||
|
||||
There are three output variants. Default (without options "-a" or
|
||||
"-V") is one line for each object. For adapters the busid and their
|
||||
There are four output variants. Default (without options "-a" or
|
||||
"-V" or "-Z") is one line for each object.
|
||||
For the zfcp device driver (module), its parameters are listed.
|
||||
For adapters the busid and their
|
||||
corresponding SCSI host names are listed. For ports the pair
|
||||
"busid"/"wwpn" and their corresponding FC-remote-port names are listed.
|
||||
For units the triple "busid"/"wwpn"/"lun" and their corresponding SCSI
|
||||
@@ -37,20 +39,53 @@ device names are listed.
|
||||
|
||||
Option "-V" additionally shows the sysfs paths of interest for the
|
||||
listed object.
|
||||
If a SCSI device has an associated block device (disk)
|
||||
and SCSI generic (sg) device, show their sysfs paths in a 3rd line.
|
||||
|
||||
Option "-a" additionally shows all attributes of interest found in
|
||||
Option "-a" additionally shows attributes of interest found in
|
||||
sysfs for the listed object.
|
||||
|
||||
Option "-m" together with option "-a" shows sysfs attributes if available for:
|
||||
host: css (subchannel); port: zfcp_port; lun: zfcp_unit.
|
||||
Specifying option "-m" twice together with option "-a"
|
||||
additionally shows SCSI device sysfs attributes
|
||||
if available for:
|
||||
scsi_disk, block, block_integrity, block_queue, block_queue_iosched.
|
||||
|
||||
Option "-e" enables an extended output format.
|
||||
It enumerates all hosts/ports/luns independent of their state.
|
||||
It also produces some improved error messages.
|
||||
The extended output format can help determining problems.
|
||||
Since a long time, without option "-e", lszfcp has
|
||||
.I
|
||||
not
|
||||
enumerated:
|
||||
hosts that have never been online (since last bind to zfcp device driver),
|
||||
hosts that are defunct (CHPID configured off, or z/VM detach),
|
||||
ports with fc_rport port_state unequal to "Online" (such as fibres pulled),
|
||||
zfcp units without associated SCSI device (unit_add of non-existent LUN).
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B -a, --attributes
|
||||
Show all attributes of the specified objects.
|
||||
Show the most interesting attributes of the specified objects.
|
||||
.TP
|
||||
.B -m, --moreattrs
|
||||
Show more attributes of the specified objects.
|
||||
Host: css (subchannel). Port: zfcp_port. Lun: zfcp_unit.
|
||||
If specified twice,
|
||||
show additional attributes of the specified SCSI device objects:
|
||||
scsi_disk, block, block_integrity, block_queue, block_queue_iosched.
|
||||
.TP
|
||||
.B -Z, --modparms
|
||||
List zfcp (module) parameters with a scope of the device driver.
|
||||
.TP
|
||||
.B -D, --devices
|
||||
List zfcp units and SCSI devices.
|
||||
.TP
|
||||
.B -H, --hosts
|
||||
List zfcp adapters and fc-hosts (default). Information is given only
|
||||
List zfcp adapters, fc-hosts, and scsi-hosts (default).
|
||||
Without option "-e", information is given only
|
||||
for adapters that are online (registered at the SCSI stack).
|
||||
.TP
|
||||
.B -P, --ports
|
||||
@@ -69,9 +104,14 @@ List zfcp port(s) and FC remote port(s) selected by wwpn. (Information
|
||||
for several ports might be shown if a remote port is configured for
|
||||
different adapters.)
|
||||
.TP
|
||||
.B -e, --extended
|
||||
Generate extended output.
|
||||
.TP
|
||||
.B -V, --verbose
|
||||
Generate verbose output. Display sysfs path names of class and bus
|
||||
devices that are of interest for this object.
|
||||
If a SCSI device has an associated block device (disk)
|
||||
and SCSI generic (sg) device, show their sysfs paths in a 3rd line.
|
||||
.TP
|
||||
.B -s, --sysfs /path/to/sys
|
||||
Use path as sysfs (for dbginfo archives).
|
||||
@@ -86,7 +126,7 @@ Display version info and exit.
|
||||
.PP
|
||||
Options "-b", "-p" or "-l" are of restricting nature. They limit the output
|
||||
to those adapters, ports or units that match the specified busid, wwpn and lun.
|
||||
If none of the options "-H", "-P" and "-D" are specified, "-b" implies "-H",
|
||||
If none of the options "-Z", "-H", "-P" and "-D" are specified, "-b" implies "-H",
|
||||
"-p" implies "-P" and "-l" implies "-D".
|
||||
|
||||
.SH EXAMPLES
|
||||
@@ -105,6 +145,8 @@ Show all adapters that match the given busid, all ports that match the given
|
||||
busid and wwpnn and show all units that match the given busid, wwpn and lun.
|
||||
.IP "lszfcp -b 0.0.0815 -p 0x5005123456789000 -l 0x0000000000000000 -H -P -D"
|
||||
Generates same output as previous example.
|
||||
.IP "lszfcp -ZHPDVeamm"
|
||||
Generates the fullest output in the extended format.
|
||||
.SH "SEE ALSO"
|
||||
.BR lscss (8)
|
||||
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
include ../../common.mak
|
||||
|
||||
all: chzcrypt lszcrypt zcryptctl
|
||||
all: chzcrypt lszcrypt zcryptctl zcryptstats
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
chzcrypt: chzcrypt.o misc.o $(libs)
|
||||
lszcrypt: lszcrypt.o misc.o $(libs)
|
||||
zcryptctl: zcryptctl.o misc.o $(libs)
|
||||
zcryptstats: zcryptstats.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptstats $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c zcryptstats.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o chzcrypt lszcrypt zcryptctl
|
||||
rm -f *.o chzcrypt lszcrypt zcryptctl zcryptstats
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.\" nroff -man lszcrypt.8
|
||||
.\" to process this source
|
||||
.\"
|
||||
.TH LSZCRYPT 8 "OCT 2017" "s390-tools"
|
||||
.TH LSZCRYPT 8 "JAN 2019" "s390-tools"
|
||||
.SH NAME
|
||||
lszcrypt \- display zcrypt device and configuration information
|
||||
.SH SYNOPSIS
|
||||
@@ -111,6 +111,68 @@ Displays help text and exits.
|
||||
.TP 8
|
||||
.B -v, --version
|
||||
Displays version information and exits.
|
||||
.SH VERBOSE LISTING DETAILS
|
||||
Some of the columns showing up in verbose listing mode may need some
|
||||
explanation:
|
||||
.TP
|
||||
.B TYPE and HWTYPE
|
||||
The HWTYPE is a numeric value showing which type of hardware the zcrypt
|
||||
device driver presumes that this crypto card is. The currently known values
|
||||
are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5 and 12=CEX6.
|
||||
.br
|
||||
The TYPE is a human readable value showing the hardware type and the basic
|
||||
function type (A=Accelerator, C=CCA Coprocessor, P=EP11 Coprocessor). So
|
||||
for example CEX6P means a CEX6 card in EP11 Coprocessor mode.
|
||||
.TP
|
||||
.B REQUESTS
|
||||
This is the counter value of successful processed requests on card or queue
|
||||
level. Successful here means the request was processed without any failure
|
||||
in the whole processing chain.
|
||||
.TP
|
||||
.B PENDING
|
||||
The underlying firmware and hardware layer usually provide some queuing
|
||||
space for requests. When this queue is already filled up, the zcrypt device
|
||||
driver maintains a software queue of pending requests. The sum of these
|
||||
both values is displayed here and shows the amount of requests waiting for
|
||||
processing on card or queue level.
|
||||
.TP
|
||||
.B FUNCTIONS
|
||||
This column shows firmware and hardware function details:
|
||||
.br
|
||||
S - APSC available: card/queue can handle requests with the special bit
|
||||
enabled.
|
||||
.br
|
||||
M - Accelerator card/queue with support for RSA ME with up to 4k key size.
|
||||
.br
|
||||
C - Accelerator card/queue with support for RSA CRT with up to 4k key size.
|
||||
.br
|
||||
D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode).
|
||||
.br
|
||||
A - Card/queue is providing Accelerator functions (this is the Accelerator mode).
|
||||
.br
|
||||
X - Card/queue is providing EP11 functions (this is the EP11 Coprocessor mode).
|
||||
.br
|
||||
N - APXA available (ability to address more than 16 crypto cards and domains).
|
||||
.br
|
||||
F - Full function support (opposed to restricted function support, see below).
|
||||
.br
|
||||
R - Restricted function support. The F and R flag both reflect if a
|
||||
hypervisor is somehow restricting this crypto resource in a virtual
|
||||
environment. Dependent on the hypervisor configuration the crypto requests
|
||||
may be filtered by the hypervisor to allow only a subset of functions
|
||||
within the virtual runtime environment. For example a shared CCA
|
||||
Coprocessor may be restricted by the hypervisor to allow only clear key
|
||||
operations within the guests.
|
||||
.TP
|
||||
.B DRIVER
|
||||
.br
|
||||
Shows which card or queue device driver currently handles this crypto
|
||||
resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX6
|
||||
hardware), cex2card/cex2cqueue (CEX2C and CEX3C hardware),
|
||||
cex2acard/cex2aqueue (CEX2A and CEX3A hardware) and vfio_ap (queue reserved
|
||||
for use by kvm hypervisor for kvm guests and not accessible to host
|
||||
applications). It is also valid to have no driver handling a queue which is
|
||||
shown as a -no-driver- entry.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B lszcrypt
|
||||
|
||||
247
zconf/zcrypt/zcryptstats.8
Normal file
247
zconf/zcrypt/zcryptstats.8
Normal file
@@ -0,0 +1,247 @@
|
||||
.\" Copyright IBM Corp. 2019
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH ZCRYPTSTATS 1 "January 2019" "s390-tools"
|
||||
.SH NAME
|
||||
zcryptstats \- Display usage statistics of IBM Crypto Express adapters
|
||||
.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B zcryptstats
|
||||
.RI [ OPTIONS ]
|
||||
.RI [ DEVICE_ID
|
||||
[...] ]
|
||||
.
|
||||
.PP
|
||||
.B zcryptstats
|
||||
.BR \-\-help | \-h
|
||||
.br
|
||||
.B zcryptstats
|
||||
.BR \-\-version | \-v
|
||||
.
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.
|
||||
Use \fBzcryptstats\fP to display usage statistics of IBM Crypto Express
|
||||
adapters.
|
||||
.PP
|
||||
\fBzcryptstats\fP obtains cryptographic performance measurement data
|
||||
periodically and displays the data for each cryptographic device for each
|
||||
interval.
|
||||
A cryptographic device can be either a card device or a queue device (APQN).
|
||||
\fBzcryptstats\fP runs forever unless you limit the number of intervals with
|
||||
the \fB\-\-count\fP option. The default interval time is 10 seconds.
|
||||
Use the \fB\-\-interval\fP option to specify a different interval time.
|
||||
.PP
|
||||
By default, all available cryptographic devices are monitored.
|
||||
You can optionally specify the device IDs of the devices to be monitored.
|
||||
The card device representation and the queue device are both in hexadecimal
|
||||
notation.
|
||||
.PP
|
||||
Use the \fB\-\-no-apqn\fP option to omit the performance measurement data of
|
||||
the queues. If the system does not support obtaining cryptographic
|
||||
performance measurement data on the queue devices, only the card devices
|
||||
are monitored.
|
||||
.PP
|
||||
For each device, a set of counters is displayed. The amount and meaning of the
|
||||
counters are dependent on the device type and mode, see the COUNTERS section.
|
||||
For each counter and interval, the following values are displayed:
|
||||
.RS 2
|
||||
.IP "\(bu" 2
|
||||
Number of measured operations.
|
||||
.IP "\(bu" 2
|
||||
Rate of the measured operation in operations per second.
|
||||
.IP "\(bu" 2
|
||||
Utilization of the device in percent.
|
||||
.IP "\(bu" 2
|
||||
Average duration of the operations.
|
||||
.RE
|
||||
.PP
|
||||
The sum of all operations is displayed in a separate \fBtotals\fP line.
|
||||
Use the \fB\-\-only-totals\fP option to omit the individual counters and
|
||||
display the totals only. Use the \fB\-\-no\-totals\fP option to omit the
|
||||
totals.
|
||||
.PP
|
||||
|
||||
.B Note:
|
||||
The utilization value of a counter can exceed 100%. This value is caused by
|
||||
the parallel execution of cryptographic operations.
|
||||
.PP
|
||||
Cryptographic performance measurement data might not be available when Linux
|
||||
is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an
|
||||
error message and exits.
|
||||
.
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.
|
||||
.TP
|
||||
.BR DEVICE_ID
|
||||
Specifies a cryptographic device for which statistics are displayed.
|
||||
A device ID can either be a card device ID
|
||||
(\fI<card-id>\fP) or a queue device (APQN) ID (\fI<card-id>.<domain-id>\fP).
|
||||
To filter all devices by domain, provide \fI.<domain-id>\fP.
|
||||
If no IDs are given, statistics are displayed for all available devices.
|
||||
.
|
||||
.TP
|
||||
.BR \-i ", " \-\-interval\~\fIINTERVAL\fP
|
||||
Specifies the interval time in seconds. If this option is omitted, then the
|
||||
default interval time of 10 seconds is used.
|
||||
.
|
||||
.TP
|
||||
.BR \-c ", " \-\-count\~\fICOUNT\fP
|
||||
Specifies the number of reports that are generated at \fIINTERVAL\fP seconds
|
||||
apart. If this option is omitted, the \fBzcryptstats\fP command generates
|
||||
reports continuously, until it is stopped with control-C.
|
||||
.
|
||||
.TP
|
||||
.BR \-o ", " \-\-output\~\fIJSON\fP|\fITABLE\fP|\fICSV\fP
|
||||
Displays the statistics in the specified format. If this option is omitted, a
|
||||
comprehensive report is displayed. Supported output formats are:
|
||||
.RS 8
|
||||
.IP "\(bu" 2
|
||||
\fBJSON:\fP Displays the statistics in Javascript Object Notation (JSON) format.
|
||||
JSON output field order is undefined, and new fields might be added in the
|
||||
future.
|
||||
.IP "\(bu" 2
|
||||
\fBTABLE:\fP Displays the statistics in a human readable simple table format.
|
||||
The individual counters are omitted, and only the totals are displayed.
|
||||
This output format implies option \fB\-\-only-totals\fP.
|
||||
.IP "\(bu" 2
|
||||
\fBCSV:\fP Displays the statistics in comma-separated values format. The values
|
||||
are separated with a semicolon. The individual counters are omitted, and only
|
||||
the totals are displayed. This output format implies option
|
||||
\fB\-\-only-totals\fP.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.BR \-t ", " \-\-no\-totals
|
||||
Excludes the totals of all counters of a card device or queue device
|
||||
(APQN). This option cannot be specified together with option
|
||||
\fB\-\-only\-totals\fP or option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP.
|
||||
.
|
||||
.TP
|
||||
.BR \-T ", " \-\-only\-totals
|
||||
Displays only the totals of all counters of a card device or a queue device
|
||||
(APQN), but not the individual counters. This option is implied with
|
||||
option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP.
|
||||
.
|
||||
.TP
|
||||
.BR \-a ", " \-\-no\-apqn
|
||||
Displays only the counters of the card device, but omits the counters of the
|
||||
queue device (APQN). If the system does not support obtaining cryptographic
|
||||
performance measurement data on the queue devices, this option is implied.
|
||||
.
|
||||
.TP
|
||||
.BR \-M ", " \-\-map\-type\~\fIMAPPING\fP
|
||||
Maps unknown cryptographic device types and modes to known types and modes.
|
||||
This option should only be used when new, so far unknown cryptographic devices
|
||||
are found. You can then map them to known devices and modes, provided that the
|
||||
new cryptographic devices report the same counters as the known cryptographic
|
||||
device to which it is mapped.
|
||||
The mapping specification consists of a comma-separated list of
|
||||
\fIFROM\-TYPE\fP:\fIFROM\-MODE\fP=\fITO\-TYPE\fP:\fITO\-MODE\fP specifications.
|
||||
The type and mode values must be specified in decimal notation.
|
||||
.
|
||||
.TP
|
||||
.BR \-A ", " \-\-all
|
||||
Displays all cards devices and queue devices (APQNs), not only those that are
|
||||
available to the Linux instance. Using this option additional cryptographic
|
||||
devices that are available in the CEC, but not available to the Linux system
|
||||
are also monitored.
|
||||
This option cannot be specified together with option \fB\-\-only-online\fP.
|
||||
.
|
||||
.TP
|
||||
.BR \-O ", " \-\-only\-online
|
||||
Displays only online cards devices and queue devices (APQNs). This option
|
||||
cannot be specified together with option \fB\-\-all\fP.
|
||||
.
|
||||
.TP
|
||||
.BR \-V ", " \-\-verbose
|
||||
Displays additional information messages during processing.
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
Displays help text and exits.
|
||||
.TP
|
||||
.BR \-v ", " \-\-version
|
||||
Displays version information and exits.
|
||||
.
|
||||
.
|
||||
.
|
||||
.SH COUNTERS
|
||||
.
|
||||
.PP
|
||||
.B IBM Crypto Express adapter in accelerator mode:
|
||||
.RS 4
|
||||
.TP
|
||||
.B All
|
||||
All operations on the adapter
|
||||
.TP
|
||||
.B RSA Key-gen
|
||||
RSA-key-generation operations (also included in \fBAll\fP).
|
||||
.RE
|
||||
.PP
|
||||
.B IBM Crypto Express adapter in CCA co-processor mode:
|
||||
.RS 4
|
||||
.TP
|
||||
.B RSA 1024 ME
|
||||
1024-bit ME-format RSA operations.
|
||||
.TP
|
||||
.B RSA 2048 ME
|
||||
2048-bit ME-format RSA operations.
|
||||
.TP
|
||||
.B RSA 1024 CRT
|
||||
1024-bit CRT-format RSA operations.
|
||||
.TP
|
||||
.B RSA 2048 CRT
|
||||
2048-bit CRT-format RSA operations.
|
||||
.TP
|
||||
.B RSA 4096 ME
|
||||
4096-bit ME-format RSA operations.
|
||||
.TP
|
||||
.B RSA 4096 CTR
|
||||
4096-bit CRT-format RSA operations.
|
||||
.RE
|
||||
.PP
|
||||
.B IBM Crypto Express adapter in EP11 co-processor mode:
|
||||
.RS 4
|
||||
.TP
|
||||
.B Asym. Slow
|
||||
Slow asymmetric-key functions.
|
||||
.TP
|
||||
.B Asym. Fast
|
||||
Fast asymmetric-key functions.
|
||||
.TP
|
||||
.B Symm. Partial
|
||||
Symmetric-key functions that return partial or incremental results.
|
||||
.TP
|
||||
.B Symm. Complete
|
||||
Symmetric-key functions that return a complete or final result.
|
||||
.TP
|
||||
.B Asym. Key-gen
|
||||
asymmetric-key generation function.
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B zcryptstats 02
|
||||
Display statistics for all cryptographic devices with card ID \fB02\fP.
|
||||
.TP
|
||||
.B zcryptstats 02.0005 --interval 5
|
||||
Display statistics for cryptographic devices with card ID \fB02\fP and domain
|
||||
ID \fB0005\fP in a 5 second interval.
|
||||
.TP
|
||||
.B zcryptstats .0005 --count 10
|
||||
Display statistics for cryptographic devices with domain ID \fB0005\fP with the
|
||||
default interval time of 10 seconds, for 10 intervals.
|
||||
.TP
|
||||
.B zcryptstats 02 --output JSON
|
||||
Display statistics for all cryptographic devices with card ID \fB02\fP in
|
||||
\fBJSON\fP output format.
|
||||
.TP
|
||||
|
||||
2418
zconf/zcrypt/zcryptstats.c
Normal file
2418
zconf/zcrypt/zcryptstats.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,10 @@ static bool is_exportable(struct setting *s, config_t config)
|
||||
/* Skip values that cannot be determined. */
|
||||
return false;
|
||||
}
|
||||
if (!attrib_check_value(a, s->value)) {
|
||||
/* Skip values that are not acceptable input values. */
|
||||
return false;
|
||||
}
|
||||
if (!attrib_match_default(s->attrib, s->value)) {
|
||||
/* All non-default values should be exported. */
|
||||
return true;
|
||||
|
||||
@@ -2,9 +2,8 @@ include ../common.mak
|
||||
|
||||
libs = $(rootdir)/libzds/libzds.a \
|
||||
$(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a \
|
||||
$(rootdir)/libdasd/libdasd.a
|
||||
$(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
ifeq (${HAVE_FUSE},0)
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ struct zdsfs_info {
|
||||
};
|
||||
|
||||
static struct zdsfs_info zdsfsinfo;
|
||||
static int zdsfs_create_meta_data_buffer(struct zdsfs_info *);
|
||||
static int zdsfs_verify_datasets(void);
|
||||
|
||||
struct zdsfs_file_info {
|
||||
struct dshandle *dsh;
|
||||
@@ -193,6 +195,48 @@ static int zdsfs_getattr(const char *path, struct stat *stbuf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zdsfs_read_device(struct dasd *newdasd, const char *device)
|
||||
{
|
||||
struct errorlog *log;
|
||||
int rc;
|
||||
|
||||
rc = dasd_disk_reserve(device);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error when reserving device %s: %s\n",
|
||||
device, strerror(rc));
|
||||
lzds_dasd_get_errorlog(newdasd, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
rc = lzds_dasd_alloc_rawvtoc(newdasd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error when reading VTOC from device %s: %s\n",
|
||||
device, strerror(rc));
|
||||
lzds_dasd_get_errorlog(newdasd, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot,
|
||||
newdasd);
|
||||
if (rc) {
|
||||
fprintf(stderr,
|
||||
"error when extracting data sets from dasd %s: %s\n",
|
||||
device, strerror(rc));
|
||||
lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
rc = dasd_disk_release(device);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error when releasing device %s: %s\n",
|
||||
device, strerror(rc));
|
||||
lzds_dasd_get_errorlog(newdasd, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs)
|
||||
{
|
||||
struct dasditerator *dasdit;
|
||||
@@ -239,6 +283,33 @@ static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int zdsfs_update_vtoc(void)
|
||||
{
|
||||
struct dasditerator *dasdit;
|
||||
struct dasd *dasd;
|
||||
int rc;
|
||||
|
||||
lzds_dslist_free(zdsfsinfo.zdsroot);
|
||||
rc = lzds_zdsroot_alloc_dasditerator(zdsfsinfo.zdsroot, &dasdit);
|
||||
if (rc)
|
||||
return -ENOMEM;
|
||||
|
||||
while (!lzds_dasditerator_get_next_dasd(dasdit, &dasd))
|
||||
zdsfs_read_device(dasd, dasd->device);
|
||||
|
||||
lzds_dasditerator_free(dasdit);
|
||||
rc = zdsfs_verify_datasets();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zdsfs_create_meta_data_buffer(&zdsfsinfo);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi))
|
||||
{
|
||||
@@ -252,6 +323,10 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
int rc;
|
||||
int ispds, issupported;
|
||||
|
||||
rc = zdsfs_update_vtoc();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* we have two type of directories
|
||||
* type one: the root directory contains all data sets
|
||||
*/
|
||||
@@ -328,6 +403,9 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi)
|
||||
goto error1;
|
||||
|
||||
if (strcmp(path, "/"METADATAFILE) == 0) {
|
||||
rc = zdsfs_update_vtoc();
|
||||
if (rc)
|
||||
return rc;
|
||||
zfi->dsh = NULL;
|
||||
zfi->is_metadata_file = 1;
|
||||
zfi->metaread = 0;
|
||||
@@ -595,6 +673,7 @@ static int zdsfs_verify_datasets(void)
|
||||
if (rc)
|
||||
return ENOMEM;
|
||||
while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) {
|
||||
lzds_dataset_get_name(ds, &dsname);
|
||||
lzds_dataset_get_is_complete(ds, &iscomplete);
|
||||
if (!iscomplete) {
|
||||
lzds_dataset_get_name(ds, &dsname);
|
||||
@@ -821,23 +900,7 @@ static void zdsfs_process_device(const char *device)
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
rc = lzds_dasd_read_rawvtoc(newdasd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error when reading VTOC from device %s:"
|
||||
" %s\n", device, strerror(rc));
|
||||
lzds_dasd_get_errorlog(newdasd, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot,
|
||||
newdasd);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error when extracting data sets from dasd %s:"
|
||||
" %s\n", device, strerror(rc));
|
||||
lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
exit(1);
|
||||
}
|
||||
zdsfs_read_device(newdasd, device);
|
||||
}
|
||||
|
||||
static void zdsfs_process_device_file(const char *devfile)
|
||||
|
||||
@@ -405,7 +405,7 @@ function check_cpuplugd {
|
||||
# check if cpuplugd is running
|
||||
# If so, the whole per-cpu mechanism of blktrace gets corrupted, which
|
||||
# results in the infamous 'bad trace magic' message
|
||||
if [ -e /var/run/cpuplugd.pid ]; then
|
||||
if [ -e /run/cpuplugd.pid ]; then
|
||||
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
|
||||
echo "$WRP_TOOLNAME: Warning: cpuplugd is running which can corrupt the traces.";
|
||||
echo " It is recommended to stop cpuplugd for the duration of the";
|
||||
@@ -514,7 +514,7 @@ function check_for_multipath_devices() {
|
||||
(( i+=2 ));
|
||||
while [[ `echo "${mp_arr[$i]:0:1}" | grep -ve "[0-9a-zA-Z]"` ]] && [ $i -lt ${#mp_arr[@]} ]; do
|
||||
if [ `echo ${mp_arr[$i]} | grep -e "[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}" | wc -l` -ne 0 ]; then
|
||||
line="`echo ${mp_arr[$i]} | sed 's/.*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`";
|
||||
line="`echo ${mp_arr[$i]} | sed 's/[^0-9]*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`";
|
||||
checked_devs[${#checked_devs[@]}]=`echo $line | awk '{print "/dev/"$2}'`;
|
||||
ddebug " adding ${checked_devs[${#checked_devs[@]}-1]}";
|
||||
WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host${line%%:*}";
|
||||
|
||||
@@ -13,10 +13,9 @@ FILES = fba0.bin fba1b.bin fba2.bin \
|
||||
eckd0_ldl.bin eckd0_cdl.bin \
|
||||
eckd1.bin eckd1b.bin eckd2.bin \
|
||||
tape0.bin \
|
||||
eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin \
|
||||
stage3.bin
|
||||
eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin
|
||||
|
||||
all: data.o data.h tape0.bin
|
||||
all: data.o data.h tape0.bin stage3.bin
|
||||
|
||||
# Prevent make from using some default rules...
|
||||
%: %.S
|
||||
@@ -44,7 +43,7 @@ eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o menu.o sclp.o \
|
||||
fba2.exec: head.o stage2.o cio.o fba2.o libc.o menu.o sclp.o \
|
||||
kdump2.o kdump.o entry.o
|
||||
stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \
|
||||
kdump.o entry.o
|
||||
kdump.o entry.o stage3.lds
|
||||
|
||||
%.exec: %.o
|
||||
@STAGE=$$( \
|
||||
@@ -71,12 +70,30 @@ stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \
|
||||
--only-section=.ex_table \
|
||||
--only-section=.data \
|
||||
--only-section=.rodata.str1.2 \
|
||||
--only-section=.rodata.cst8 \
|
||||
--only-section=.rodata \
|
||||
--only-section=.stage2dump.tail \
|
||||
--only-section=.eckd2dump_mv.tail \
|
||||
--only-section=.fixup \
|
||||
$< $@
|
||||
|
||||
stage3.bin: stage3.exec
|
||||
$(OBJCOPY) -O binary \
|
||||
--only-section=.stage2.head \
|
||||
--only-section=.text.dummy \
|
||||
--only-section=.text.start \
|
||||
--only-section=.text \
|
||||
--only-section=.ex_table \
|
||||
--only-section=.data \
|
||||
--only-section=.rodata.str1.2 \
|
||||
--only-section=.rodata.cst8 \
|
||||
--only-section=.rodata \
|
||||
--only-section=.stage2dump.tail \
|
||||
--only-section=.eckd2dump_mv.tail \
|
||||
--only-section=.fixup \
|
||||
--pad-to=0xf000 \
|
||||
$< $@
|
||||
|
||||
data.o: $(FILES)
|
||||
$(LD) $(NO_PIE_LDFLAGS) -r -b binary -o data.o $(FILES)
|
||||
|
||||
@@ -86,6 +103,7 @@ data.h: data.o
|
||||
echo "extern char $$SYMBOL;" >>data.h; done
|
||||
|
||||
clean:
|
||||
rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy
|
||||
rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \
|
||||
stage3.bin
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
/* Internal error */
|
||||
#define EINTERNAL 0x00004511
|
||||
|
||||
/* Secure IPL error */
|
||||
#define ESECUREBOOT 0x00004512
|
||||
|
||||
/* kdump: No operating system information was found */
|
||||
#define EOS_INFO_MISSING 0x00004520
|
||||
|
||||
|
||||
@@ -58,6 +58,26 @@ void *memcpy(void *dest, const void *src, unsigned long n)
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move @n bytes of memory from @src to @dest. The memory regions may overlap.
|
||||
*/
|
||||
void *memmove(void *dest, const void *src, unsigned long n)
|
||||
{
|
||||
const char *s = src;
|
||||
char *d = dest;
|
||||
|
||||
if (s < d) {
|
||||
d += n;
|
||||
s += n;
|
||||
while (n--)
|
||||
*--d = *--s;
|
||||
} else {
|
||||
while (n--)
|
||||
*d++ = *s++;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy string
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,7 @@ typedef unsigned char uint8_t;
|
||||
void printf(const char *, ...);
|
||||
void sprintf(char *, const char *, ...);
|
||||
void *memcpy(void *, const void *, unsigned long);
|
||||
void *memmove(void *, const void *, unsigned long);
|
||||
void *memset(void *, int c, unsigned long);
|
||||
char *strcat(char *, const char *);
|
||||
int strncmp(const char *, const char *, unsigned long);
|
||||
|
||||
@@ -35,6 +35,11 @@ struct psw_t {
|
||||
uint64_t addr;
|
||||
} __aligned(8);
|
||||
|
||||
struct psw32_t {
|
||||
uint32_t mask;
|
||||
uint32_t addr;
|
||||
} __aligned(8);
|
||||
|
||||
void load_wait_psw(uint64_t, struct psw_t *);
|
||||
|
||||
struct _lowcore {
|
||||
|
||||
@@ -115,8 +115,13 @@ void start(void)
|
||||
/* skip header */
|
||||
entry = (struct component_entry *)
|
||||
(load_address + sizeof(struct component_header));
|
||||
|
||||
while (entry->type == COMPONENT_LOAD) {
|
||||
while (entry->type == COMPONENT_LOAD ||
|
||||
entry->type == COMPONENT_SIGNATURE) {
|
||||
if (entry->type == COMPONENT_SIGNATURE) {
|
||||
/* Skip unhandled signature components */
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
load_address = (void *)(unsigned long)
|
||||
entry->address.load_address[1];
|
||||
load_blocklist(entry, subchannel_id, load_address);
|
||||
|
||||
@@ -61,7 +61,8 @@ struct component_entry {
|
||||
|
||||
typedef enum {
|
||||
COMPONENT_EXECUTE = 0x01,
|
||||
COMPONENT_LOAD = 0x02
|
||||
COMPONENT_LOAD = 0x02,
|
||||
COMPONENT_SIGNATURE = 0x03
|
||||
} component_type;
|
||||
|
||||
struct stage2_descr {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
* 0x2000-0x4fff Sections (load): head, text, data, rodata, rodata.str
|
||||
* 0x5000-0x51ff eckd2dump_mv parameter block (426 bytes)
|
||||
* 0x5200-0x5fff Sections: bss
|
||||
* 0x6000-0x9fff Memory allocation (heap)
|
||||
* 0x6000-0x8fff Memory allocation (heap)
|
||||
* 0x9000-0x9fff Memory to load stage3 parameter to
|
||||
* 0xa000-0xdfff Memory to load stage3 to
|
||||
* 0xe000-0xffff Stack
|
||||
*
|
||||
@@ -52,7 +53,7 @@ SECTIONS
|
||||
|
||||
. = 0x6000;
|
||||
__heap_start = .;
|
||||
. = 0xa000;
|
||||
. = 0x9000;
|
||||
__heap_stop = .;
|
||||
|
||||
. = 0xf000;
|
||||
|
||||
@@ -12,35 +12,15 @@
|
||||
#include "libc.h"
|
||||
#include "s390.h"
|
||||
#include "stage3.h"
|
||||
#include "error.h"
|
||||
|
||||
/*
|
||||
* 48 Byte dummy space for external symbols
|
||||
* _parm_addr; address of parmline
|
||||
* _initrd_addr; address of initrd
|
||||
* _initrd_len; length of initrd
|
||||
* _load_psw; load psw of kernel
|
||||
* _extra_parm; use extra parm line mechanism?
|
||||
* stage3_flags; flags (e.g. STAGE3_FLAG_KDUMP)
|
||||
*
|
||||
* needed to blow up the binary and leave room
|
||||
*/
|
||||
__attribute__ ((section(".text.dummy"))) void _dummy(void)
|
||||
{
|
||||
asm volatile(
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
".long 0x00000000\n"
|
||||
);
|
||||
}
|
||||
#define for_each_rb_entry(entry, rb) \
|
||||
for (entry = rb->entries; \
|
||||
(void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
|
||||
entry++)
|
||||
|
||||
static const char *msg_sipl_inval = "Secure boot failure: invalid load address";
|
||||
static const char *msg_sipl_unverified = "Secure boot failure: unverified load address";
|
||||
|
||||
static unsigned char ebc_037[256] = {
|
||||
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||
@@ -221,6 +201,64 @@ start_kernel(void)
|
||||
: [psw] "a" (psw) );
|
||||
}
|
||||
|
||||
unsigned int
|
||||
is_verified_address(unsigned long image_addr)
|
||||
{
|
||||
struct ipl_rb_component_entry *comp;
|
||||
struct ipl_rb_components *comps;
|
||||
struct ipl_pl_hdr *pl_hdr;
|
||||
struct ipl_rl_hdr *rl_hdr;
|
||||
struct ipl_rb_hdr *rb_hdr;
|
||||
unsigned long tmp;
|
||||
void *rl_end;
|
||||
|
||||
/*
|
||||
* There is an IPL report, to find it load the pointer to the
|
||||
* IPL parameter information block from lowcore and skip past
|
||||
* the IPL parameter list, then align the address to a double
|
||||
* word boundary.
|
||||
*/
|
||||
tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
|
||||
pl_hdr = (struct ipl_pl_hdr *) tmp;
|
||||
tmp = (tmp + pl_hdr->len + 7) & -8UL;
|
||||
rl_hdr = (struct ipl_rl_hdr *) tmp;
|
||||
/* Walk through the IPL report blocks in the IPL Report list */
|
||||
comps = NULL;
|
||||
rl_end = (void *) rl_hdr + rl_hdr->len;
|
||||
rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
|
||||
while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
|
||||
(void *) rb_hdr + rb_hdr->len <= rl_end) {
|
||||
switch (rb_hdr->rbt) {
|
||||
case IPL_RBT_COMPONENTS:
|
||||
comps = (struct ipl_rb_components *) rb_hdr;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
rb_hdr = (void *) rb_hdr + rb_hdr->len;
|
||||
}
|
||||
for_each_rb_entry(comp, comps) {
|
||||
if (image_addr == comp->addr &&
|
||||
comp->flags & IPL_RB_COMPONENT_FLAG_SIGNED &&
|
||||
comp->flags & IPL_RB_COMPONENT_FLAG_VERIFIED)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
secure_boot_enabled()
|
||||
{
|
||||
struct ipl_pl_hdr *pl_hdr;
|
||||
unsigned long tmp;
|
||||
|
||||
tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
|
||||
pl_hdr = (struct ipl_pl_hdr *) tmp;
|
||||
|
||||
return pl_hdr->flags & IPL_FLAG_SECURE;
|
||||
}
|
||||
|
||||
void start(void)
|
||||
{
|
||||
unsigned int subchannel_id;
|
||||
@@ -228,6 +266,26 @@ void start(void)
|
||||
unsigned char *command_line = (unsigned char *)COMMAND_LINE;
|
||||
unsigned int begin = 0, end = 0, length = 0;
|
||||
|
||||
/*
|
||||
* IPL process is secure we have to use default IPL values and
|
||||
* check if the psw jump address is within at the start of a
|
||||
* verified component. If it is not IPL is aborted.
|
||||
*/
|
||||
if (secure_boot_enabled()) {
|
||||
if (_image_addr != DEFAULT_IMAGE_ADDR ||
|
||||
_load_psw != DEFAULT_PSW_LOAD)
|
||||
panic(ESECUREBOOT, "%s", msg_sipl_inval);
|
||||
|
||||
if (!is_verified_address(_load_psw & PSW_ADDR_MASK))
|
||||
panic(ESECUREBOOT, "%s", msg_sipl_unverified);
|
||||
}
|
||||
/*
|
||||
* cut the kernel header
|
||||
*/
|
||||
memmove((void *)_image_addr,
|
||||
(void *)_image_addr + KERNEL_HEADER_SIZE,
|
||||
_image_len - KERNEL_HEADER_SIZE);
|
||||
|
||||
/* store subchannel ID into low core and into new kernel space */
|
||||
subchannel_id = S390_lowcore.subchannel_id;
|
||||
*(unsigned int *)__LC_IPLDEV = subchannel_id;
|
||||
|
||||
@@ -27,14 +27,156 @@
|
||||
#define STAGE3_FLAG_SCSI 0x0001000000000000ULL
|
||||
#define STAGE3_FLAG_KDUMP 0x0002000000000000ULL
|
||||
|
||||
#define IPL_FLAG_SECURE 0x40
|
||||
|
||||
#define DEFAULT_IMAGE_ADDR 0x10000
|
||||
#define DEFAULT_PSW_LOAD 0x0008000080010000L
|
||||
#define PSW_ADDR_MASK 0x000000007FFFFFFFL
|
||||
#define KERNEL_HEADER_SIZE 65536
|
||||
|
||||
#define UNSPECIFIED_ADDRESS -1ULL
|
||||
|
||||
|
||||
/* IPL Parameter List header */
|
||||
struct ipl_pl_hdr {
|
||||
uint32_t len;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t version;
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block header */
|
||||
struct ipl_pb_hdr {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block 0 with common fields */
|
||||
struct ipl_pb0_common {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[84];
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block 0 for FCP */
|
||||
struct ipl_pb0_fcp {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t reserved1[3];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[304];
|
||||
uint8_t opt;
|
||||
uint8_t reserved3[3];
|
||||
uint8_t cssid;
|
||||
uint8_t reserved4[1];
|
||||
uint8_t devno;
|
||||
uint8_t reserved5[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved6[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved7[260];
|
||||
uint8_t scp_data[];
|
||||
} __packed;
|
||||
|
||||
/* IPL Parameter Block 0 for CCW */
|
||||
struct ipl_pb0_ccw {
|
||||
uint32_t len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t loadparm[8];
|
||||
uint8_t reserved2[84];
|
||||
uint16_t reserved3 : 13;
|
||||
uint8_t ssid : 3;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved4[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved5[8];
|
||||
} __packed;
|
||||
|
||||
struct ipl_parameter_block {
|
||||
struct ipl_pl_hdr hdr;
|
||||
union {
|
||||
struct ipl_pb_hdr pb0_hdr;
|
||||
struct ipl_pb0_common common;
|
||||
struct ipl_pb0_fcp fcp;
|
||||
struct ipl_pb0_ccw ccw;
|
||||
char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
|
||||
};
|
||||
} __packed __aligned(PAGE_SIZE);
|
||||
|
||||
/* IPL Report List header */
|
||||
struct ipl_rl_hdr {
|
||||
uint32_t len;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[2];
|
||||
uint8_t version;
|
||||
uint8_t reserved2[8];
|
||||
} __packed;
|
||||
|
||||
/* IPL Report Block header */
|
||||
struct ipl_rb_hdr {
|
||||
uint32_t len;
|
||||
uint8_t rbt;
|
||||
uint8_t reserved1[11];
|
||||
} __packed;
|
||||
|
||||
/* IPL Report Block types */
|
||||
enum ipl_rbt {
|
||||
IPL_RBT_CERTIFICATES = 1,
|
||||
IPL_RBT_COMPONENTS = 2,
|
||||
};
|
||||
|
||||
/* IPL Report Block for the certificate list */
|
||||
struct ipl_rb_certificate_entry {
|
||||
uint64_t addr;
|
||||
uint64_t len;
|
||||
} __packed;
|
||||
|
||||
struct ipl_rb_certificates {
|
||||
uint32_t len;
|
||||
uint8_t rbt;
|
||||
uint8_t reserved1[11];
|
||||
struct ipl_rb_certificate_entry entries[];
|
||||
} __packed;
|
||||
|
||||
/* IPL Report Block for the component list */
|
||||
struct ipl_rb_component_entry {
|
||||
uint64_t addr;
|
||||
uint64_t len;
|
||||
uint8_t flags;
|
||||
uint8_t reserved1[5];
|
||||
uint16_t certificate_index;
|
||||
uint8_t reserved2[8];
|
||||
};
|
||||
|
||||
#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80
|
||||
#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40
|
||||
|
||||
struct ipl_rb_components {
|
||||
uint32_t len;
|
||||
uint8_t rbt;
|
||||
uint8_t reserved1[11];
|
||||
struct ipl_rb_component_entry entries[];
|
||||
} __packed;
|
||||
|
||||
extern unsigned long long _parm_addr; /* address of parmline */
|
||||
extern unsigned long long _initrd_addr; /* address of initrd */
|
||||
extern unsigned long long _initrd_len; /* length of initrd */
|
||||
extern unsigned long long _load_psw; /* load psw of kernel */
|
||||
extern unsigned long long _extra_parm; /* use extra parm line mechanism? */
|
||||
extern unsigned long long stage3_flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */
|
||||
extern unsigned long long _image_len; /* length of kernel */
|
||||
extern unsigned long long _image_addr; /* target address of kernel */
|
||||
extern void kdump_stage3();
|
||||
|
||||
#endif /* STAGE3_H */
|
||||
|
||||
@@ -1,33 +1,47 @@
|
||||
/*
|
||||
* Memory layout for stage 3
|
||||
* =========================
|
||||
*
|
||||
* General memory layout
|
||||
* ---------------------
|
||||
*
|
||||
* 0x0000-0x1fff Lowcore
|
||||
* 0x2000-0x5fff Memory allocation (heap)
|
||||
* 0x6000-0x8fff free
|
||||
* 0x9000-0x9fff Stage3 parameter
|
||||
* 0xa000-0xdfff Stage3 code + data
|
||||
* 0xe000-0xffff Stack
|
||||
*/
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x0;
|
||||
|
||||
. = 0x7000;
|
||||
. = 0x2000;
|
||||
__heap_start = .;
|
||||
. = 0xa000;
|
||||
. = 0x6000;
|
||||
__heap_stop = .;
|
||||
|
||||
. = 0xa000;
|
||||
|
||||
/* stage 3 parameter */
|
||||
. = 0x9000;
|
||||
_parm_addr = .;
|
||||
. = 0xa008;
|
||||
. = 0x9008;
|
||||
_initrd_addr = .;
|
||||
. = 0xa010;
|
||||
. = 0x9010;
|
||||
_initrd_len = .;
|
||||
. = 0xa018;
|
||||
. = 0x9018;
|
||||
_load_psw = .;
|
||||
. = 0xa020;
|
||||
. = 0x9020;
|
||||
_extra_parm = .;
|
||||
. = 0xa028;
|
||||
. = 0x9028;
|
||||
stage3_flags =.;
|
||||
. = 0x9030;
|
||||
_image_len = .;
|
||||
. = 0x9038;
|
||||
_image_addr = .;
|
||||
|
||||
. = 0xa000;
|
||||
.text.dummy : { *(.text.dummy) }
|
||||
|
||||
. = 0xa050;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
__ex_table_start = .;
|
||||
@@ -35,11 +49,9 @@ SECTIONS
|
||||
__ex_table_stop = .;
|
||||
.eh_frame : { *(.eh_frame) }
|
||||
|
||||
. = 0xc000;
|
||||
__bss_start = .;
|
||||
.bss : { *(.bss) }
|
||||
__bss_stop = .;
|
||||
.rodata : {*(.rodata) }
|
||||
.data : { *(.data) }
|
||||
|
||||
}
|
||||
|
||||
@@ -248,6 +248,9 @@ struct boot_stage3_params {
|
||||
uint64_t load_psw;
|
||||
uint64_t extra_parm;
|
||||
uint16_t flags;
|
||||
uint16_t reserved[3];
|
||||
uint64_t image_len;
|
||||
uint64_t image_addr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define STAGE3_FLAG_SCSI 0x0001
|
||||
@@ -305,10 +308,10 @@ int boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b,
|
||||
disk_blockptr_t *stage2_list,
|
||||
blocknum_t stage2_count);
|
||||
int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job);
|
||||
size_t get_stage3_size();
|
||||
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);
|
||||
int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
|
||||
address_t initrd_addr, size_t initrd_len,
|
||||
address_t load_addr, int extra_parm, uint16_t flags,
|
||||
size_t image_len);
|
||||
int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
|
||||
address_t initrd_addr, address_t image_addr);
|
||||
int boot_get_tape_dump(void** data, size_t* size, uint64_t mem);
|
||||
|
||||
@@ -16,6 +16,37 @@
|
||||
#include "job.h"
|
||||
#include "zipl.h"
|
||||
|
||||
#define SIGNATURE_MAGIC "~Module signature appended~\n"
|
||||
#define PKCS7_FORMAT 0x01
|
||||
|
||||
struct signature_header {
|
||||
uint8_t format;
|
||||
uint8_t reserved[3];
|
||||
uint32_t length;
|
||||
} __attribute((packed));
|
||||
|
||||
typedef union {
|
||||
uint64_t load_address;
|
||||
uint64_t load_psw;
|
||||
struct signature_header sig_head;
|
||||
} component_data;
|
||||
|
||||
/*
|
||||
* The file_signature structure and the PKEY_ID definition
|
||||
* are based on linux/scripts/sign-file.c
|
||||
*/
|
||||
struct file_signature {
|
||||
u8 algorithm;
|
||||
u8 hash;
|
||||
u8 id_type;
|
||||
u8 signer_len;
|
||||
u8 key_id_len;
|
||||
u8 __pad[3];
|
||||
u32 sig_len;
|
||||
char magic[28];
|
||||
};
|
||||
|
||||
#define PKEY_ID_PKCS7 0x02
|
||||
|
||||
int bootmap_create(struct job_data* job, disk_blockptr_t* program_table,
|
||||
disk_blockptr_t *scsi_dump_sb_blockptr,
|
||||
|
||||
@@ -121,6 +121,7 @@ struct job_data {
|
||||
int add_files;
|
||||
int dry_run;
|
||||
int command_line;
|
||||
int is_secure;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ int misc_check_readable_file(const char* filename);
|
||||
int misc_check_writable_device(const char* devno, int blockdev, int chardev);
|
||||
void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to);
|
||||
void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to);
|
||||
unsigned int misc_check_secure_boot(void);
|
||||
|
||||
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
#define SCAN_SECTION_NUM 9
|
||||
#define SCAN_KEYWORD_NUM 21
|
||||
#define SCAN_KEYWORD_NUM 22
|
||||
#define SCAN_KEYWORD_ONLY_NUM 1
|
||||
#define SCAN_AUTOMENU_NAME "zipl-automatic-menu"
|
||||
|
||||
@@ -51,6 +51,7 @@ enum scan_keyword_id {
|
||||
scan_keyword_targetoffset = 18,
|
||||
scan_keyword_defaultauto = 19,
|
||||
scan_keyword_kdump = 20,
|
||||
scan_keyword_secure = 21,
|
||||
};
|
||||
|
||||
enum scan_section_type {
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
#define DISK_LAYOUT_ID 0x00000001
|
||||
|
||||
#define ZIPL_STAGE2_LOAD_ADDRESS 0x2000
|
||||
#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa050LL
|
||||
#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa000LL
|
||||
#define DEFAULT_IMAGE_ADDRESS 0x10000LL
|
||||
#define KDUMP_IMAGE_ADDRESS 0x10010LL
|
||||
#define DEFAULT_STAGE3_ADDRESS 0xa000LL
|
||||
#define DEFAULT_STAGE3_PARAMS_ADDRESS 0x9000LL
|
||||
#define MINIMUM_ADDRESS 0x10000LL
|
||||
#define ADDRESS_LIMIT 0x80000000LL
|
||||
#define ADDRESS_LIMIT_KDUMP 0x2000000UL /* HSA size: 32 MiB */
|
||||
@@ -31,11 +32,15 @@
|
||||
#define MAXIMUM_PARMLINE_SIZE 0x380
|
||||
#define MAXIMUM_PHYSICAL_BLOCKSIZE 0x1000
|
||||
|
||||
#define STAGE3_HEAP_SIZE 0x4000
|
||||
#define STAGE3_HEAP_ADDRESS 0x2000
|
||||
#define STAGE3_STACK_SIZE 0x1000
|
||||
#define STAGE3_STACK_ADDRESS 0xF000
|
||||
|
||||
#define PSW_ADDRESS_MASK 0x000000007fffffffLL
|
||||
#define PSW_LOAD 0x0008000080000000LL
|
||||
#define PSW_DISABLED_WAIT 0x000a000000000000LL
|
||||
|
||||
#define KERNEL_HEADER_SIZE 65536
|
||||
#define BOOTMAP_FILENAME "bootmap"
|
||||
#define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX"
|
||||
|
||||
@@ -44,12 +49,18 @@
|
||||
#define ZIPL_CONF_VAR "ZIPLCONF"
|
||||
#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
|
||||
#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
|
||||
#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin"
|
||||
#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure"
|
||||
|
||||
#define MENU_DEFAULT_PROMPT 0
|
||||
#define MENU_DEFAULT_TIMEOUT 0
|
||||
|
||||
#define MAX_DUMP_VOLUMES 32
|
||||
|
||||
#define SECURE_BOOT_DISABLED 0
|
||||
#define SECURE_BOOT_ENABLED 1
|
||||
#define SECURE_BOOT_AUTO 2
|
||||
|
||||
/* Internal component load address type */
|
||||
typedef uint64_t address_t;
|
||||
|
||||
|
||||
@@ -352,6 +352,21 @@ whether they contain a dump signature or not.
|
||||
This option can only be used together with
|
||||
.BR \-\-mvdump .
|
||||
|
||||
.TP
|
||||
.BR "\-S <SWITCH>" " or " "\-\-secure <SWITCH>"
|
||||
Control the zIPL secure boot support.
|
||||
<SWITCH> can take one of three values:
|
||||
|
||||
auto (default)
|
||||
Write signatures if available and supported by the system.
|
||||
1
|
||||
Signatures are written independent of support indicated by the local
|
||||
system. Also missing signatures for stage 3 and kernel IPL files
|
||||
will result in an error.
|
||||
0
|
||||
No signatures will be written.
|
||||
|
||||
|
||||
.SH EXAMPLE
|
||||
1. Scenario: prepare disk for booting a Linux kernel image using the
|
||||
following parameters:
|
||||
|
||||
@@ -82,6 +82,8 @@ below).
|
||||
.br
|
||||
defaultmenu = menu1
|
||||
.br
|
||||
secure = auto
|
||||
.br
|
||||
|
||||
[linux]
|
||||
.br
|
||||
@@ -517,6 +519,31 @@ An optional hexadecimal address may be provided to load the kernel to a
|
||||
non-default memory location.
|
||||
.PP
|
||||
|
||||
.B secure
|
||||
=
|
||||
.IR auto / 1 / 0
|
||||
(configuration only)
|
||||
.IP
|
||||
.B Configuration section:
|
||||
.br
|
||||
Control the zIPL secure boot support.
|
||||
Set this option to one of the following:
|
||||
.IP " - " 12
|
||||
.BR auto:
|
||||
Write signatures if available and supported by the system.
|
||||
.IP " - " 12
|
||||
.BR 1:
|
||||
Signatures are written independent of support indicated by the local system.
|
||||
Also missing signatures for stage 3 and kernel IPL files will result in an error.
|
||||
.IP " - " 12
|
||||
.BR 0:
|
||||
No signatures will be written.
|
||||
|
||||
The default value for
|
||||
.B 'secure'
|
||||
is auto.
|
||||
.PP
|
||||
|
||||
.B segment
|
||||
=
|
||||
.IR segment\-file , address
|
||||
|
||||
@@ -7,16 +7,16 @@ ALL_CPPFLAGS += -I../include -I../boot \
|
||||
-D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS)
|
||||
ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS)
|
||||
|
||||
libs = $(rootdir)/libu2s/libu2s.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
libs = $(rootdir)/libutil/libutil.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 = $(basename $(wildcard zipl_helper.*.c))
|
||||
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
|
||||
zipl_stage3 = ../boot/stage3.bin
|
||||
|
||||
all: zipl $(chreipl_helpers)
|
||||
all: zipl $(chreipl_helpers) $(zipl_stage3)
|
||||
|
||||
zipl: $(objects) $(libs)
|
||||
|
||||
@@ -33,6 +33,7 @@ install: all
|
||||
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)
|
||||
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
|
||||
$(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl
|
||||
@@ -48,3 +49,6 @@ clean:
|
||||
|
||||
../boot/data.o:
|
||||
make -C ../boot data.o
|
||||
|
||||
../boot/stage3.bin:
|
||||
make -C ../boot stage3.bin
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../boot/data.h"
|
||||
#include "boot.h"
|
||||
@@ -68,20 +70,17 @@ boot_check_data(void)
|
||||
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.
|
||||
/*
|
||||
* Create a stage 3 parameter block 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. */
|
||||
* BYTECOUNT to contain the parameter block 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)
|
||||
boot_get_stage3_parms(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,
|
||||
size_t image_len)
|
||||
{
|
||||
struct boot_stage3_params params;
|
||||
void* data;
|
||||
@@ -92,9 +91,10 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
|
||||
return -1;
|
||||
}
|
||||
/* Get memory */
|
||||
data = misc_malloc(DATA_SIZE(stage3));
|
||||
data = misc_malloc(sizeof(params));
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
memset(data, 0, sizeof(params));
|
||||
/* Prepare params section */
|
||||
params.parm_addr = (uint64_t) parm_addr;
|
||||
params.initrd_addr = (uint64_t) initrd_addr;
|
||||
@@ -102,11 +102,12 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
|
||||
params.load_psw = (uint64_t)(image_addr | PSW_LOAD);
|
||||
params.extra_parm = (uint64_t) extra_parm;
|
||||
params.flags = flags;
|
||||
params.image_len = (uint64_t) image_len;
|
||||
params.image_addr = (uint64_t) image_addr;
|
||||
/* Initialize buffer */
|
||||
memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3));
|
||||
memcpy(data, ¶ms, sizeof(struct boot_stage3_params));
|
||||
memcpy(data, ¶ms, sizeof(params));
|
||||
*buffer = data;
|
||||
*bytecount = DATA_SIZE(stage3);
|
||||
*bytecount = sizeof(params);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lib/util_part.h"
|
||||
#include "lib/util_path.h"
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootmap.h"
|
||||
@@ -117,6 +118,25 @@ check_menu_positions(struct job_menu_data* menu, char* name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_secure_boot_support(void)
|
||||
{
|
||||
unsigned int val;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(ZIPL_SIPL_PATH, "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
if (fscanf(fp, "%d", &val) != 1) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return val ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/* Write COUNT elements of the blocklist specified by LIST as a linked list
|
||||
* of segment table blocks to the file identified by file descriptor FD. Upon
|
||||
@@ -199,24 +219,21 @@ add_program_table(int fd, disk_blockptr_t* table, int entries,
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
struct component_entry {
|
||||
uint8_t data[23];
|
||||
uint8_t type;
|
||||
union {
|
||||
uint64_t load_address;
|
||||
uint64_t load_psw;
|
||||
} address;
|
||||
component_data compdat;
|
||||
} __attribute((packed));
|
||||
|
||||
typedef enum {
|
||||
component_execute = 0x01,
|
||||
component_load = 0x02
|
||||
component_load = 0x02,
|
||||
component_signature = 0x03
|
||||
} component_type;
|
||||
|
||||
static void
|
||||
create_component_entry(void* buffer, disk_blockptr_t* pointer,
|
||||
component_type type, uint64_t address,
|
||||
component_type type, component_data data,
|
||||
struct disk_info* info)
|
||||
{
|
||||
struct component_entry* entry;
|
||||
@@ -228,10 +245,15 @@ create_component_entry(void* buffer, disk_blockptr_t* pointer,
|
||||
case component_load:
|
||||
bootmap_store_blockptr(&entry->data, pointer,
|
||||
info);
|
||||
entry->address.load_address = address;
|
||||
entry->compdat.load_address = data.load_address;
|
||||
break;
|
||||
case component_execute:
|
||||
entry->address.load_psw = address;
|
||||
entry->compdat.load_psw = data.load_psw;
|
||||
break;
|
||||
case component_signature:
|
||||
bootmap_store_blockptr(&entry->data, pointer,
|
||||
info);
|
||||
entry->compdat.sig_head = data.sig_head;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -267,7 +289,7 @@ struct component_loc {
|
||||
|
||||
static int
|
||||
add_component_file(int fd, const char* filename, address_t load_address,
|
||||
off_t offset, void* component, int add_files,
|
||||
size_t trailer, void *component, int add_files,
|
||||
struct disk_info* info, struct job_target_data* target,
|
||||
struct component_loc *location)
|
||||
{
|
||||
@@ -279,8 +301,6 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
||||
size_t size;
|
||||
blocknum_t count;
|
||||
int rc;
|
||||
int from;
|
||||
unsigned int to;
|
||||
|
||||
if (add_files) {
|
||||
/* Read file to buffer */
|
||||
@@ -289,17 +309,10 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
||||
error_text("Could not read file '%s'", filename);
|
||||
return rc;
|
||||
}
|
||||
/* Ensure minimum size */
|
||||
if (size <= (size_t) offset) {
|
||||
error_reason("File '%s' is too small (has to be "
|
||||
"greater than %ld bytes)", filename,
|
||||
(long) offset);
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
size -= trailer;
|
||||
/* Write buffer */
|
||||
count = disk_write_block_buffer(fd, 0, buffer + offset,
|
||||
size - offset, &list, info);
|
||||
count = disk_write_block_buffer(fd, 0, buffer,
|
||||
size, &list, info);
|
||||
free(buffer);
|
||||
if (count == 0) {
|
||||
error_text("Could not write to bootmap file");
|
||||
@@ -321,20 +334,7 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
||||
disk_free_info(file_info);
|
||||
if (count == 0)
|
||||
return -1;
|
||||
if (count * info->phy_block_size <= (size_t) offset) {
|
||||
error_reason("File '%s' is too small (has to be "
|
||||
"greater than %ld bytes)", filename,
|
||||
(long) offset);
|
||||
free(list);
|
||||
return -1;
|
||||
}
|
||||
if (offset > 0) {
|
||||
/* Shorten list by offset */
|
||||
from = offset / info->phy_block_size;
|
||||
count -= from;
|
||||
for (to=0; to < count; to++, from++)
|
||||
list[to] = list[from];
|
||||
}
|
||||
count -= DIV_ROUND_UP(trailer, info->phy_block_size);
|
||||
}
|
||||
/* Fill in component location */
|
||||
loc.addr = load_address;
|
||||
@@ -346,7 +346,7 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
||||
free(list);
|
||||
if (rc == 0) {
|
||||
create_component_entry(component, &segment, component_load,
|
||||
load_address, info);
|
||||
(component_data) load_address, info);
|
||||
/* Return location if requested */
|
||||
if (location != NULL)
|
||||
*location = loc;
|
||||
@@ -354,11 +354,10 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
add_component_buffer(int fd, void* buffer, size_t size, address_t load_address,
|
||||
add_component_buffer(int fd, void* buffer, size_t size, component_data data,
|
||||
void* component, struct disk_info* info,
|
||||
struct component_loc *location)
|
||||
struct component_loc *location, int type)
|
||||
{
|
||||
struct component_loc loc;
|
||||
disk_blockptr_t segment;
|
||||
@@ -372,17 +371,21 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address,
|
||||
error_text("Could not write to bootmap file");
|
||||
return -1;
|
||||
}
|
||||
/* Fill in component location */
|
||||
loc.addr = load_address;
|
||||
loc.size = count * info->phy_block_size;
|
||||
if (type == component_load) {
|
||||
/* Fill in component location */
|
||||
loc.addr = data.load_address;
|
||||
loc.size = count * info->phy_block_size;
|
||||
} else {
|
||||
loc.addr = 0;
|
||||
loc.size = 0;
|
||||
}
|
||||
/* Try to compact list */
|
||||
count = disk_compact_blocklist(list, count, info);
|
||||
/* Write segment table */
|
||||
rc = add_segment_table(fd, list, count, &segment, info);
|
||||
free(list);
|
||||
if (rc == 0) {
|
||||
create_component_entry(component, &segment, component_load,
|
||||
load_address, info);
|
||||
create_component_entry(component, &segment, type, data, info);
|
||||
/* Return location if requested */
|
||||
if (location != NULL)
|
||||
*location = loc;
|
||||
@@ -391,6 +394,30 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
add_dummy_buffer(int fd, size_t size, address_t addr, void *component,
|
||||
struct disk_info *info, struct component_loc *comp_loc)
|
||||
{
|
||||
char *buffer;
|
||||
int rc;
|
||||
|
||||
buffer = misc_malloc(size);
|
||||
if (buffer == NULL)
|
||||
return -1;
|
||||
|
||||
memset(buffer, 0, size);
|
||||
rc = add_component_buffer(fd, buffer, size,
|
||||
(component_data) (uint64_t) addr,
|
||||
component, info, comp_loc, component_load);
|
||||
if (rc) {
|
||||
free(buffer);
|
||||
return rc;
|
||||
}
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_components(const char *name[], struct component_loc *loc, int num)
|
||||
{
|
||||
@@ -409,23 +436,86 @@ print_components(const char *name[], struct component_loc *loc, int num)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
extract_signature(char *filename, void **ret_signature,
|
||||
struct signature_header *sig_head)
|
||||
{
|
||||
struct file_signature *file_sig;
|
||||
size_t signature_size = 0;
|
||||
void *signature;
|
||||
char *buffer;
|
||||
size_t size;
|
||||
|
||||
if (misc_read_file(filename, &buffer, &size, 0))
|
||||
return 0;
|
||||
|
||||
file_sig = (void *) buffer + size - sizeof(*file_sig);
|
||||
if (memcmp(file_sig->magic, SIGNATURE_MAGIC, sizeof(file_sig->magic))
|
||||
!= 0)
|
||||
goto out;
|
||||
|
||||
signature = misc_malloc(file_sig->sig_len);
|
||||
if (signature == NULL)
|
||||
goto out;
|
||||
signature_size = file_sig->sig_len;
|
||||
|
||||
memcpy(signature, buffer + size - signature_size - sizeof(*file_sig),
|
||||
signature_size);
|
||||
|
||||
*ret_signature = signature;
|
||||
sig_head->length = signature_size;
|
||||
|
||||
switch (file_sig->id_type) {
|
||||
case PKEY_ID_PKCS7:
|
||||
sig_head->format = PKCS7_FORMAT;
|
||||
break;
|
||||
default:
|
||||
error_text("Unsupported signature type %02x",
|
||||
file_sig->id_type);
|
||||
signature_size = 0;
|
||||
goto out;
|
||||
}
|
||||
/* return size of signature and corresponding header */
|
||||
signature_size += sizeof(*file_sig);
|
||||
out:
|
||||
free(buffer);
|
||||
return signature_size;
|
||||
}
|
||||
|
||||
static void
|
||||
check_remaining_filesize(size_t filesize, size_t signature_size,
|
||||
struct disk_info *info, char *filename)
|
||||
{
|
||||
if ((filesize - signature_size) % info->phy_block_size) {
|
||||
fprintf(stderr,
|
||||
"Warning: Size of signed file %s is not a multiple of the disk block size\n",
|
||||
filename);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
|
||||
int verbose, int add_files, component_header_type type,
|
||||
struct disk_info* info, struct job_target_data* target)
|
||||
struct disk_info* info, struct job_target_data* target,
|
||||
int is_secure)
|
||||
{
|
||||
struct stat stats;
|
||||
void* table;
|
||||
void* stage3;
|
||||
size_t stage3_size;
|
||||
const char *comp_name[4] = {"kernel image", "parmline",
|
||||
"initial ramdisk", "internal loader"};
|
||||
struct component_loc comp_loc[4];
|
||||
int rc;
|
||||
struct component_loc comp_loc[10];
|
||||
struct signature_header sig_head;
|
||||
size_t ramdisk_size, image_size;
|
||||
bool secure_boot_supported;
|
||||
size_t stage3_params_size;
|
||||
const char *comp_name[10];
|
||||
size_t signature_size;
|
||||
int offset, flags = 0;
|
||||
void *stage3_params;
|
||||
struct stat stats;
|
||||
void *signature;
|
||||
int comp_nr = 0;
|
||||
void *table;
|
||||
int rc;
|
||||
|
||||
memset(comp_loc, 0, sizeof(comp_loc));
|
||||
memset(&sig_head, 0, sizeof(sig_head));
|
||||
table = misc_malloc(info->phy_block_size);
|
||||
if (table == NULL)
|
||||
return -1;
|
||||
@@ -456,55 +546,189 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (info->type == disk_type_scsi)
|
||||
ramdisk_size = stats.st_size;
|
||||
if (info->type == disk_type_scsi) {
|
||||
flags |= STAGE3_FLAG_SCSI;
|
||||
/*
|
||||
* Add dummy components for stage 3 heap and stack to block the
|
||||
* associated memory areas against firmware use.
|
||||
*/
|
||||
rc = add_dummy_buffer(fd, STAGE3_HEAP_SIZE, STAGE3_HEAP_ADDRESS,
|
||||
VOID_ADD(table, offset), info,
|
||||
&comp_loc[comp_nr]);
|
||||
if (rc) {
|
||||
error_text("Could not add stage3 HEAP dummy");
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
comp_name[comp_nr] = "heap area";
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_nr++;
|
||||
rc = add_dummy_buffer(fd, STAGE3_STACK_SIZE,
|
||||
STAGE3_STACK_ADDRESS,
|
||||
VOID_ADD(table, offset), info,
|
||||
&comp_loc[comp_nr]);
|
||||
if (rc) {
|
||||
error_text("Could not add stage3 STACK dummy");
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
comp_name[comp_nr] = "stack area";
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_nr++;
|
||||
}
|
||||
if (ipl->is_kdump)
|
||||
flags |= STAGE3_FLAG_KDUMP;
|
||||
|
||||
/* Get kernel file size */
|
||||
if (stat(ipl->image, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
error_text("Could not get information for file '%s'",
|
||||
ipl->image);
|
||||
free(table);
|
||||
return -1;
|
||||
}
|
||||
image_size = stats.st_size;
|
||||
secure_boot_supported = check_secure_boot_support();
|
||||
signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature,
|
||||
&sig_head);
|
||||
if (signature_size &&
|
||||
(is_secure == SECURE_BOOT_ENABLED ||
|
||||
(is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) {
|
||||
if (verbose)
|
||||
printf(" signature for.....: %s\n", ZIPL_STAGE3_PATH);
|
||||
|
||||
rc = add_component_buffer(fd, signature, sig_head.length,
|
||||
(component_data)sig_head,
|
||||
VOID_ADD(table, offset), info,
|
||||
&comp_loc[comp_nr],
|
||||
component_signature);
|
||||
if (rc) {
|
||||
error_text("Could not add stage3 signature");
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
comp_name[comp_nr] = "loader signature";
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_nr++;
|
||||
free(signature);
|
||||
} else if (is_secure == SECURE_BOOT_ENABLED) {
|
||||
/*
|
||||
* If secure boot is forced and we have failed to extract a
|
||||
* signature for the stage 3 loader zipl will abort with an
|
||||
* error message
|
||||
*/
|
||||
error_text("Could not install Secure Boot IPL records");
|
||||
error_reason("Missing signature in internal loader file %s",
|
||||
ZIPL_STAGE3_PATH);
|
||||
free(table);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Add stage 3 loader to bootmap */
|
||||
rc = boot_get_stage3(&stage3, &stage3_size, ipl->parm_addr,
|
||||
ipl->ramdisk_addr, (size_t) stats.st_size,
|
||||
ipl->is_kdump ? ipl->image_addr + 0x10 :
|
||||
ipl->image_addr,
|
||||
(info->type == disk_type_scsi) ? 0 : 1,
|
||||
flags);
|
||||
rc = add_component_file(fd, ZIPL_STAGE3_PATH, DEFAULT_STAGE3_ADDRESS,
|
||||
signature_size, VOID_ADD(table, offset), 1,
|
||||
info, target, &comp_loc[comp_nr]);
|
||||
if (rc) {
|
||||
error_text("Could not add internal loader file '%s'",
|
||||
ZIPL_STAGE3_PATH);
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_name[comp_nr] = "internal loader";
|
||||
comp_nr++;
|
||||
|
||||
/* Add stage 3 parameter to bootmap */
|
||||
rc = boot_get_stage3_parms(&stage3_params, &stage3_params_size,
|
||||
ipl->parm_addr, ipl->ramdisk_addr,
|
||||
ramdisk_size,
|
||||
ipl->is_kdump ? ipl->image_addr + 0x10 :
|
||||
ipl->image_addr,
|
||||
(info->type == disk_type_scsi) ? 0 : 1,
|
||||
flags, image_size);
|
||||
if (rc) {
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
rc = add_component_buffer(fd, stage3, stage3_size,
|
||||
DEFAULT_STAGE3_ADDRESS,
|
||||
VOID_ADD(table, offset), info, &comp_loc[3]);
|
||||
free(stage3);
|
||||
rc = add_component_buffer(fd, stage3_params, stage3_params_size,
|
||||
(component_data) (uint64_t)
|
||||
DEFAULT_STAGE3_PARAMS_ADDRESS,
|
||||
VOID_ADD(table, offset), info,
|
||||
&comp_loc[comp_nr], component_load);
|
||||
free(stage3_params);
|
||||
if (rc) {
|
||||
error_text("Could not add stage 3 boot loader");
|
||||
error_text("Could not add parameters");
|
||||
free(table);
|
||||
return -1;
|
||||
}
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_name[comp_nr] = "parameters";
|
||||
comp_nr++;
|
||||
|
||||
/* Add kernel image */
|
||||
if (verbose) {
|
||||
printf(" kernel image......: %s\n", ipl->image);
|
||||
}
|
||||
signature_size = extract_signature(ipl->image, &signature, &sig_head);
|
||||
if (signature_size &&
|
||||
(is_secure == SECURE_BOOT_ENABLED ||
|
||||
(is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) {
|
||||
if (verbose)
|
||||
printf(" signature for.....: %s\n", ipl->image);
|
||||
|
||||
rc = add_component_buffer(fd, signature, sig_head.length,
|
||||
(component_data)sig_head,
|
||||
VOID_ADD(table, offset), info,
|
||||
&comp_loc[comp_nr],
|
||||
component_signature);
|
||||
if (rc) {
|
||||
error_text("Could not add image signature");
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
comp_name[comp_nr] = "image signature";
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_nr++;
|
||||
free(signature);
|
||||
check_remaining_filesize(image_size, signature_size, info,
|
||||
ipl->image);
|
||||
} else if (is_secure == SECURE_BOOT_ENABLED) {
|
||||
/*
|
||||
* If secure boot is forced and we have failed to extract a
|
||||
* signature for the kernel image zipl will abort with an
|
||||
* error message
|
||||
*/
|
||||
error_text("Could not install Secure Boot IPL records");
|
||||
error_reason("Missing signature in image file %s",
|
||||
ipl->image);
|
||||
free(table);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = add_component_file(fd, ipl->image, ipl->image_addr,
|
||||
KERNEL_HEADER_SIZE, VOID_ADD(table, offset),
|
||||
add_files, info, target, &comp_loc[0]);
|
||||
signature_size, VOID_ADD(table, offset),
|
||||
add_files, info, target, &comp_loc[comp_nr]);
|
||||
if (rc) {
|
||||
error_text("Could not add image file '%s'", ipl->image);
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_name[comp_nr] = "kernel image";
|
||||
comp_nr++;
|
||||
|
||||
/* Add kernel parmline */
|
||||
if (ipl->parmline != NULL) {
|
||||
/* Add kernel parmline */
|
||||
if (verbose) {
|
||||
printf(" kernel parmline...: '%s'\n", ipl->parmline);
|
||||
}
|
||||
rc = add_component_buffer(fd, ipl->parmline,
|
||||
strlen(ipl->parmline) + 1,
|
||||
ipl->parm_addr,
|
||||
(component_data) ipl->parm_addr,
|
||||
VOID_ADD(table, offset),
|
||||
info, &comp_loc[1]);
|
||||
info, &comp_loc[comp_nr],
|
||||
component_load);
|
||||
if (rc) {
|
||||
error_text("Could not add parmline '%s'",
|
||||
ipl->parmline);
|
||||
@@ -512,14 +736,45 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
|
||||
return -1;
|
||||
}
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_name[comp_nr] = "parmline";
|
||||
comp_nr++;
|
||||
}
|
||||
|
||||
/* finally add ramdisk */
|
||||
if (ipl->ramdisk != NULL) {
|
||||
signature_size = extract_signature(ipl->ramdisk, &signature,
|
||||
&sig_head);
|
||||
if (signature_size &&
|
||||
(is_secure == SECURE_BOOT_ENABLED ||
|
||||
(is_secure == SECURE_BOOT_AUTO &&
|
||||
secure_boot_supported))) {
|
||||
if (verbose) {
|
||||
printf(" signature for.....: %s\n",
|
||||
ipl->ramdisk);
|
||||
}
|
||||
rc = add_component_buffer(fd, signature,
|
||||
sig_head.length,
|
||||
(component_data)sig_head,
|
||||
VOID_ADD(table, offset), info,
|
||||
&comp_loc[comp_nr],
|
||||
component_signature);
|
||||
if (rc) {
|
||||
error_text("Could not add ramdisk signature");
|
||||
free(table);
|
||||
return rc;
|
||||
}
|
||||
comp_name[comp_nr] = "ramdisk signature";
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_nr++;
|
||||
free(signature);
|
||||
check_remaining_filesize(ramdisk_size, signature_size,
|
||||
info, ipl->ramdisk);
|
||||
}
|
||||
rc = add_component_file(fd, ipl->ramdisk,
|
||||
ipl->ramdisk_addr, 0,
|
||||
ipl->ramdisk_addr, signature_size,
|
||||
VOID_ADD(table, offset),
|
||||
add_files, info, target, &comp_loc[2]);
|
||||
add_files, info, target,
|
||||
&comp_loc[comp_nr]);
|
||||
if (rc) {
|
||||
error_text("Could not add ramdisk '%s'",
|
||||
ipl->ramdisk);
|
||||
@@ -527,13 +782,16 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
|
||||
return -1;
|
||||
}
|
||||
offset += sizeof(struct component_entry);
|
||||
comp_name[comp_nr] = "initial ramdisk";
|
||||
comp_nr++;
|
||||
}
|
||||
if (verbose)
|
||||
print_components(comp_name, comp_loc, 4);
|
||||
print_components(comp_name, comp_loc, comp_nr);
|
||||
/* Terminate component table */
|
||||
create_component_entry(VOID_ADD(table, offset), NULL,
|
||||
component_execute,
|
||||
ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD,
|
||||
(component_data) (uint64_t)
|
||||
(ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD),
|
||||
info);
|
||||
/* Write component table */
|
||||
rc = disk_write_block_aligned(fd, table, info->phy_block_size,
|
||||
@@ -584,7 +842,8 @@ if (rc) {
|
||||
print_components(comp_name, comp_loc, 1);
|
||||
/* Terminate component table */
|
||||
create_component_entry(VOID_ADD(table, offset), NULL,
|
||||
component_execute, PSW_DISABLED_WAIT, info);
|
||||
component_execute, (component_data) (uint64_t)
|
||||
PSW_DISABLED_WAIT, info);
|
||||
/* Write component table */
|
||||
rc = disk_write_block_aligned(fd, table, info->phy_block_size,
|
||||
program, info);
|
||||
@@ -674,7 +933,7 @@ add_dump_program(int fd, struct job_dump_data* dump,
|
||||
return rc;
|
||||
ipl.parm_addr = dump->parm_addr;
|
||||
return add_ipl_program(fd, &ipl, program, verbose, 1,
|
||||
type, info, target);
|
||||
type, info, target, SECURE_BOOT_DISABLED);
|
||||
}
|
||||
|
||||
|
||||
@@ -711,7 +970,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
|
||||
rc = add_ipl_program(fd, &job->data.ipl, &table[0],
|
||||
verbose || job->command_line,
|
||||
job->add_files, component_header,
|
||||
info, &job->target);
|
||||
info, &job->target, job->is_secure);
|
||||
break;
|
||||
case job_segment:
|
||||
if (job->command_line)
|
||||
@@ -762,7 +1021,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
|
||||
&table[job->data.menu.entry[i].pos],
|
||||
verbose || job->command_line,
|
||||
job->add_files, component_header,
|
||||
info, &job->target);
|
||||
info, &job->target,
|
||||
job->is_secure);
|
||||
break;
|
||||
case job_print_usage:
|
||||
case job_print_version:
|
||||
@@ -913,7 +1173,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
|
||||
ulong size;
|
||||
ulong unused_size;
|
||||
|
||||
size = DIV_ROUND_UP(get_stage3_size(), info->phy_block_size);
|
||||
/* Use approximated stage 3 size as starting point */
|
||||
size = MINIMUM_ADDRESS;
|
||||
|
||||
/* Ramdisk */
|
||||
if (job->data.dump.ramdisk != NULL) {
|
||||
if (stat(job->data.dump.ramdisk, &st))
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/u2s.h"
|
||||
#include "lib/util_sys.h"
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootmap.h"
|
||||
@@ -1132,7 +1132,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
parm.param[i].num_heads = info[i]->geo.heads;
|
||||
parm.param[i].blocksize = info[i]->phy_block_size >> 8;
|
||||
parm.param[i].devno = info[i]->devno;
|
||||
if (u2s_getbusid(device[i], busid)) {
|
||||
if (util_sys_get_dev_addr(device[i], busid) != 0) {
|
||||
error_text("Could not find bus-ID for '%s'", device[i]);
|
||||
rc = -1;
|
||||
goto out;
|
||||
|
||||
@@ -52,11 +52,12 @@ static struct option options[] = {
|
||||
{ "dry-run", no_argument, NULL, '0'},
|
||||
{ "force", no_argument, NULL, 'f'},
|
||||
{ "kdump", required_argument, NULL, 'k'},
|
||||
{ "secure", required_argument, NULL, 'S'},
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/* Command line option abbreviations */
|
||||
static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:";
|
||||
static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:";
|
||||
|
||||
struct command_line {
|
||||
char* data[SCAN_KEYWORD_NUM];
|
||||
@@ -215,6 +216,11 @@ get_command_line(int argc, char* argv[], struct command_line* line)
|
||||
} else
|
||||
cmdline.menu = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
is_keyword = 1;
|
||||
rc = store_option(&cmdline, scan_keyword_secure,
|
||||
optarg);
|
||||
break;
|
||||
case 'h':
|
||||
cmdline.help = 1;
|
||||
break;
|
||||
@@ -511,7 +517,7 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
|
||||
/* Fill in component data */
|
||||
num = 0;
|
||||
rc = set_cl_element(&cl[num++], "kernel image", ipl->image,
|
||||
&ipl->image_addr, 0, 0x10000,
|
||||
&ipl->image_addr, 0, 0,
|
||||
MAXIMUM_PHYSICAL_BLOCKSIZE);
|
||||
if (rc)
|
||||
goto error;
|
||||
@@ -1263,6 +1269,26 @@ type_from_target(char *target, disk_type_t *type)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
set_secure_ipl(char *keyword, struct job_data *job)
|
||||
{
|
||||
if (strcmp(keyword, "auto") == 0) {
|
||||
job->is_secure = SECURE_BOOT_AUTO;
|
||||
} else if (strcmp(keyword, "0") == 0) {
|
||||
job->is_secure = SECURE_BOOT_DISABLED;
|
||||
} else if (strcmp(keyword, "1") == 0) {
|
||||
if (job->target.targettype != disk_type_scsi) {
|
||||
error_reason("Secure boot forced for non-SCSI disk type");
|
||||
return -1;
|
||||
}
|
||||
job->is_secure = SECURE_BOOT_ENABLED;
|
||||
} else {
|
||||
error_reason("Invalid secure boot setting '%s'",
|
||||
keyword);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_job_from_section_data(char* data[], struct job_data* job, char* section)
|
||||
@@ -1345,6 +1371,13 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Fill in secure boot */
|
||||
if (data[(int) scan_keyword_secure] != NULL) {
|
||||
rc = set_secure_ipl(data[(int) scan_keyword_secure],
|
||||
job);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
case section_ipl_tape:
|
||||
/* Tape IPL job */
|
||||
@@ -1502,6 +1535,13 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job)
|
||||
job->data.menu.timeout =
|
||||
atol(scan[i].content.keyword.value);
|
||||
break;
|
||||
case scan_keyword_secure:
|
||||
rc = set_secure_ipl(
|
||||
scan[i].content.keyword.value,
|
||||
job);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
case scan_keyword_target:
|
||||
job->target.bootmap_dir = misc_strdup(
|
||||
scan[i].content.keyword.value);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "error.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
/* Allocate SIZE bytes of memory. Upon success, return pointer to memory.
|
||||
* Return NULL otherwise. */
|
||||
void *
|
||||
|
||||
@@ -45,45 +45,45 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = {
|
||||
* ult to tofs e mete file isk ent et pt out ultm dump
|
||||
* rs enu
|
||||
*
|
||||
* targ targ targ targ targ defa kdum
|
||||
* etba etty etge etbl etof ulta p
|
||||
* targ targ targ targ targ defa kdum secu
|
||||
* etba etty etge etbl etof ulta p re
|
||||
* se pe omet ocks fset uto
|
||||
* ry ize
|
||||
*/
|
||||
/* default auto */
|
||||
{opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv,
|
||||
opt, opt, opt, opt, opt, opt, inv},
|
||||
opt, opt, opt, opt, opt, opt, inv, opt},
|
||||
/* default menu */
|
||||
{inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, opt},
|
||||
/* default section */
|
||||
{req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, opt},
|
||||
/* ipl */
|
||||
{inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv,
|
||||
opt, opt, opt, opt, opt, inv, opt},
|
||||
opt, opt, opt, opt, opt, inv, opt, opt},
|
||||
/* segment load */
|
||||
{inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* part dump */
|
||||
{inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* fs dump */
|
||||
{inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* ipl tape */
|
||||
{inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv,
|
||||
inv, inv, inv, inv, inv, inv, inv},
|
||||
inv, inv, inv, inv, inv, inv, inv, inv},
|
||||
/* multi volume dump */
|
||||
{inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req,
|
||||
inv, inv, inv, inv, inv, inv, inv}
|
||||
inv, inv, inv, inv, inv, inv, inv, inv}
|
||||
};
|
||||
|
||||
/* Determines which keyword may be present in a menu section */
|
||||
enum scan_key_state scan_menu_key_table[SCAN_KEYWORD_NUM] = {
|
||||
/* menu section */
|
||||
opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv,
|
||||
opt, opt, opt, opt, opt, inv, inv
|
||||
opt, opt, opt, opt, opt, inv, inv, opt
|
||||
};
|
||||
|
||||
/* Mapping of keyword IDs to strings */
|
||||
@@ -111,6 +111,7 @@ static const struct {
|
||||
{ "timeout", scan_keyword_timeout},
|
||||
{ "tape", scan_keyword_tape},
|
||||
{ "kdump", scan_keyword_kdump},
|
||||
{ "secure", scan_keyword_secure},
|
||||
};
|
||||
|
||||
/* List of keywords that are used without an assignment */
|
||||
@@ -1863,6 +1864,7 @@ scan_build_automenu(struct scan_token* scan)
|
||||
/* defaultmenu */ 1 +
|
||||
/* menu heading */ 1 +
|
||||
/* keyword default,prompt,timeout */ 3 +
|
||||
/* keyword secure */ 1 +
|
||||
/* target keywords*/ num_targets +
|
||||
/* missing target definitions */ num_sections * num_targets +
|
||||
/* number assigment */ num_sections;
|
||||
@@ -1978,6 +1980,14 @@ scan_build_automenu(struct scan_token* scan)
|
||||
db_keyword[i]))
|
||||
goto err;
|
||||
}
|
||||
/* secure= */
|
||||
i = (int) scan_keyword_secure;
|
||||
if (db_keyword[i]) {
|
||||
if (scan_append_keyword_assignment(new_scan, &i_new,
|
||||
scan_keyword_secure,
|
||||
db_keyword[i]))
|
||||
goto err;
|
||||
}
|
||||
/* target= */
|
||||
/* targetbase= */
|
||||
/* targetgeometry= */
|
||||
|
||||
307
zkey/keystore.c
307
zkey/keystore.c
@@ -259,13 +259,13 @@ static int _keystore_set_file_permission(struct keystore *keystore,
|
||||
|
||||
if (chmod(filename, keystore->mode) != 0) {
|
||||
rc = -errno;
|
||||
warnx("chmod faild on file '%s': %s", filename, strerror(-rc));
|
||||
warnx("chmod failed on file '%s': %s", filename, strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (chown(filename, geteuid(), keystore->owner) != 0) {
|
||||
rc = -errno;
|
||||
warnx("chown faild on file '%s': %s", filename, strerror(-rc));
|
||||
warnx("chown failed on file '%s': %s", filename, strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*check_association_t)(const char *value, bool remove,
|
||||
typedef int (*check_association_t)(const char *value, bool remove, bool set,
|
||||
char **normalized, void *private);
|
||||
|
||||
/**
|
||||
@@ -407,7 +407,7 @@ static int _keystore_set_association(struct properties *key_props,
|
||||
|
||||
for (i = 0; newvals[i] != NULL; i++) {
|
||||
if (check_func != NULL) {
|
||||
rc = check_func(newvals[i], 0, &normalized,
|
||||
rc = check_func(newvals[i], 0, 1, &normalized,
|
||||
check_private);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
@@ -488,7 +488,7 @@ static int _keystore_add_association(struct properties *key_props,
|
||||
|
||||
for (i = 0; newvals[i] != NULL; i++) {
|
||||
if (check_func != NULL) {
|
||||
rc = check_func(newvals[i], 0, &normalized,
|
||||
rc = check_func(newvals[i], 0, 0, &normalized,
|
||||
check_private);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
@@ -567,7 +567,7 @@ static int _keystore_remove_association(struct properties *key_props,
|
||||
|
||||
for (i = 0; delvals[i] != NULL; i++) {
|
||||
if (check_func != NULL) {
|
||||
rc = check_func(delvals[i], 1, &normalized,
|
||||
rc = check_func(delvals[i], 1, 0, &normalized,
|
||||
check_private);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
@@ -1085,20 +1085,27 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct apqn_check {
|
||||
bool noonlinecheck;
|
||||
bool nomsg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks an APQN value for its syntax. This is a callback function for
|
||||
* function _keystore_change_association().
|
||||
*
|
||||
* @param[in] apqn the APQN value to check
|
||||
* @param[in] remove if true the apqn is removed
|
||||
* @param[in] set if true the apqn is set (not used here)
|
||||
* @param[out] normalized normalized value on return or NULL if no change
|
||||
* @param[in] private private data (not used here)
|
||||
* @param[in] private private data (struct apqn_check)
|
||||
*
|
||||
* @returns 0 if successful, a negative errno value otherwise
|
||||
*/
|
||||
static int _keystore_apqn_check(const char *apqn, bool remove,
|
||||
char **normalized, void *UNUSED(private))
|
||||
static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set),
|
||||
char **normalized, void *private)
|
||||
{
|
||||
struct apqn_check *info = (struct apqn_check *)private;
|
||||
int rc, card, domain;
|
||||
regmatch_t pmatch[1];
|
||||
regex_t reg_buf;
|
||||
@@ -1124,15 +1131,16 @@ static int _keystore_apqn_check(const char *apqn, bool remove,
|
||||
|
||||
util_asprintf(normalized, "%02x.%04x", card, domain);
|
||||
|
||||
if (remove) {
|
||||
if (remove || info->noonlinecheck) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = _keystore_is_apqn_online(card, domain);
|
||||
if (rc != 1) {
|
||||
warnx("The APQN %02x.%04x is %s", card, domain,
|
||||
rc == -1 ? "not a CCA card" : "not online");
|
||||
if (info->nomsg == 0)
|
||||
warnx("The APQN %02x.%04x is %s", card, domain,
|
||||
rc == -1 ? "not a CCA card" : "not online");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
} else {
|
||||
@@ -1149,6 +1157,7 @@ struct volume_check {
|
||||
struct keystore *keystore;
|
||||
const char *name;
|
||||
const char *volume;
|
||||
bool set;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1173,6 +1182,11 @@ static int _keystore_volume_check_process(struct keystore *UNUSED(keystore),
|
||||
{
|
||||
struct volume_check *info = (struct volume_check *)private;
|
||||
|
||||
if (info->set) {
|
||||
if (strcmp(name, info->name) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
warnx("Key '%s' is already associated with volume '%s'", name,
|
||||
info->volume);
|
||||
return -EINVAL;
|
||||
@@ -1204,12 +1218,13 @@ static int _keystore_is_block_device(const char *volume)
|
||||
*
|
||||
* @param[in] volume the Volume value to check
|
||||
* @param[in] remove if true the volume is removed
|
||||
* @param[in] set if true the volume is set
|
||||
* @param[out] normalized normalized value on return or NULL if no change
|
||||
* @param[in] private private data: struct volume_check
|
||||
*
|
||||
* @returns 0 if successful, a negative errno value otherwise
|
||||
*/
|
||||
static int _keystore_volume_check(const char *volume, bool remove,
|
||||
static int _keystore_volume_check(const char *volume, bool remove, bool set,
|
||||
char **normalized, void *private)
|
||||
{
|
||||
struct volume_check *info = (struct volume_check *)private;
|
||||
@@ -1250,6 +1265,7 @@ static int _keystore_volume_check(const char *volume, bool remove,
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->set = set;
|
||||
rc = _keystore_process_filtered(info->keystore, NULL, info->volume,
|
||||
NULL, NULL,
|
||||
_keystore_volume_check_process, info);
|
||||
@@ -1543,6 +1559,8 @@ static int _keystore_set_default_properties(struct properties *key_props)
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] apqns a comma separated list of APQNs associated with this
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
|
||||
* of two and in range 512 - 4096 bytes. 0 means that
|
||||
* the sector size is not specified and the system
|
||||
@@ -1554,10 +1572,14 @@ static int _keystore_create_info_file(struct keystore *keystore,
|
||||
const struct key_filenames *filenames,
|
||||
const char *description,
|
||||
const char *volumes, const char *apqns,
|
||||
bool noapqncheck,
|
||||
size_t sector_size,
|
||||
const char *volume_type)
|
||||
{
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = name };
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = name,
|
||||
.set = 0 };
|
||||
struct apqn_check apqn_check = { .noonlinecheck = noapqncheck,
|
||||
.nomsg = 0 };
|
||||
struct properties *key_props;
|
||||
char temp[10];
|
||||
int rc;
|
||||
@@ -1583,7 +1605,8 @@ static int _keystore_create_info_file(struct keystore *keystore,
|
||||
|
||||
rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
|
||||
apqns != NULL ? apqns : "",
|
||||
"APQN", _keystore_apqn_check, NULL);
|
||||
"APQN", _keystore_apqn_check,
|
||||
&apqn_check);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
@@ -1642,15 +1665,18 @@ out:
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a card/domain pair from the specified APQns, or uses AUTOSELECT
|
||||
* if no APQNs are specified.
|
||||
* Extracts an online card/domain pair from the specified APQns. If none of the
|
||||
* specified APQNs are online, then -ENODEV is returned.
|
||||
* If no APQNs are specified at all, then it uses AUTOSELECT and returns zero.
|
||||
*/
|
||||
static int _keystore_get_card_domain(const char *apqns, unsigned int *card,
|
||||
unsigned int *domain)
|
||||
{
|
||||
struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 };
|
||||
char **apqn_list;
|
||||
char *normalized = NULL;
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
*card = AUTOSELECT;
|
||||
*domain = AUTOSELECT;
|
||||
@@ -1662,17 +1688,23 @@ static int _keystore_get_card_domain(const char *apqns, unsigned int *card,
|
||||
if (apqn_list[0] == NULL)
|
||||
goto out;
|
||||
|
||||
rc = _keystore_apqn_check(apqn_list[0], 0, &normalized, NULL);
|
||||
if (normalized != NULL)
|
||||
free(normalized);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
for (i = 0; apqn_list[i] != NULL; i++) {
|
||||
rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized,
|
||||
&apqn_check);
|
||||
if (normalized != NULL)
|
||||
free(normalized);
|
||||
if (rc == -EINVAL)
|
||||
goto out;
|
||||
if (rc != 0)
|
||||
continue;
|
||||
|
||||
if (sscanf(apqn_list[0], "%x.%x", card, domain) != 2) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2)
|
||||
goto found;
|
||||
}
|
||||
|
||||
warnx("None of the specified APQNs is online or of type CCA");
|
||||
rc = -ENODEV;
|
||||
found:
|
||||
out:
|
||||
str_list_free_string_array(apqn_list);
|
||||
return rc;
|
||||
@@ -1688,6 +1720,8 @@ out:
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] apqns a comma separated list of APQNs associated with this
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
|
||||
* of two and in range 512 - 4096 bytes. 0 means that
|
||||
* the sector size is not specified and the system
|
||||
@@ -1704,9 +1738,10 @@ out:
|
||||
*/
|
||||
int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, size_t sector_size,
|
||||
size_t keybits, bool xts, const char *clear_key_file,
|
||||
const char *volume_type, int pkey_fd)
|
||||
const char *apqns, bool noapqncheck,
|
||||
size_t sector_size, size_t keybits, bool xts,
|
||||
const char *clear_key_file, const char *volume_type,
|
||||
int pkey_fd)
|
||||
{
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
struct properties *key_props = NULL;
|
||||
@@ -1748,7 +1783,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
|
||||
rc = _keystore_create_info_file(keystore, name, &file_names,
|
||||
description, volumes, apqns,
|
||||
sector_size, volume_type);
|
||||
noapqncheck, sector_size, volume_type);
|
||||
if (rc != 0)
|
||||
goto out_free_props;
|
||||
|
||||
@@ -1781,6 +1816,8 @@ out_free_key_filenames:
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] apqns a comma separated list of APQNs associated with this
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
|
||||
* of two and in range 512 - 4096 bytes. 0 means that
|
||||
* the sector size is not specified and the system
|
||||
@@ -1792,7 +1829,7 @@ out_free_key_filenames:
|
||||
*/
|
||||
int keystore_import_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, size_t sector_size,
|
||||
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||
const char *import_file, const char *volume_type)
|
||||
{
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
@@ -1832,7 +1869,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||
|
||||
rc = _keystore_create_info_file(keystore, name, &file_names,
|
||||
description, volumes, apqns,
|
||||
sector_size, volume_type);
|
||||
noapqncheck, sector_size, volume_type);
|
||||
if (rc != 0)
|
||||
goto out_free_props;
|
||||
|
||||
@@ -1870,6 +1907,8 @@ out_free_key_filenames:
|
||||
* key, or an APQN prefixed with '+' or '-' to add or
|
||||
* remove that APQN respectively. If NULL then the APQNs
|
||||
* are not changed.
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
|
||||
* of two and in range 512 - 4096 bytes. 0 means that
|
||||
* the sector size is not specified and the system
|
||||
@@ -1883,10 +1922,13 @@ out_free_key_filenames:
|
||||
*/
|
||||
int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, long int sector_size,
|
||||
const char *volume_type)
|
||||
const char *apqns, bool noapqncheck,
|
||||
long int sector_size, const char *volume_type)
|
||||
{
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = name };
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = name,
|
||||
.set = 0 };
|
||||
struct apqn_check apqn_check = { .noonlinecheck = noapqncheck,
|
||||
.nomsg = 0 };
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
struct properties *key_props = NULL;
|
||||
char temp[30];
|
||||
@@ -1931,7 +1973,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
if (apqns != NULL) {
|
||||
rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
|
||||
apqns, "APQN",
|
||||
_keystore_apqn_check, NULL);
|
||||
_keystore_apqn_check,
|
||||
&apqn_check);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
}
|
||||
@@ -1982,10 +2025,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = _keystore_set_file_permission(keystore, file_names.info_filename);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
pr_verbose(keystore, "Successfully changed key '%s'", name);
|
||||
|
||||
out:
|
||||
@@ -2269,6 +2308,7 @@ static void _keystore_print_record(struct util_rec *rec,
|
||||
struct validate_info {
|
||||
struct util_rec *rec;
|
||||
int pkey_fd;
|
||||
bool noapqncheck;
|
||||
unsigned long int num_valid;
|
||||
unsigned long int num_invalid;
|
||||
unsigned long int num_warnings;
|
||||
@@ -2422,8 +2462,9 @@ static int _keystore_process_validate(struct keystore *keystore,
|
||||
"master key\n", 0);
|
||||
info->num_warnings++;
|
||||
}
|
||||
if (_keystore_display_apqn_status(properties, name) != 0)
|
||||
info->num_warnings++;
|
||||
if (info->noapqncheck == 0)
|
||||
if (_keystore_display_apqn_status(properties, name) != 0)
|
||||
info->num_warnings++;
|
||||
if (_keystore_display_volume_status(properties, name) != 0)
|
||||
info->num_warnings++;
|
||||
|
||||
@@ -2439,11 +2480,16 @@ out:
|
||||
*
|
||||
* @param[in] keystore the key store
|
||||
* @param[in] name_filter the name filter to select the key (can be NULL)
|
||||
* @param[in] apqn_filter the APQN filter to select the key (can be NULL)
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[in] pkey_fd the file descriptor of /dev/pkey
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
*/
|
||||
int keystore_validate_key(struct keystore *keystore, const char *name_filter,
|
||||
const char *apqn_filter, int pkey_fd)
|
||||
const char *apqn_filter, bool noapqncheck,
|
||||
int pkey_fd)
|
||||
{
|
||||
struct validate_info info;
|
||||
struct util_rec *rec;
|
||||
@@ -2454,6 +2500,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter,
|
||||
rec = _keystore_setup_record(1);
|
||||
|
||||
info.pkey_fd = pkey_fd;
|
||||
info.noapqncheck = noapqncheck;
|
||||
info.rec = rec;
|
||||
info.num_valid = 0;
|
||||
info.num_invalid = 0;
|
||||
@@ -2683,10 +2730,6 @@ static int _keystore_process_reencipher(struct keystore *keystore,
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
rc = _keystore_set_file_permission(keystore, out_file);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
if (params.complete || params.inplace == 1) {
|
||||
rc = _keystore_set_timestamp_property(properties,
|
||||
PROP_NAME_REENC_TIME);
|
||||
@@ -2712,11 +2755,6 @@ static int _keystore_process_reencipher(struct keystore *keystore,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = _keystore_set_file_permission(keystore,
|
||||
file_names->info_filename);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
util_asprintf(&temp, "The following LUKS2 volumes are "
|
||||
"encrypted with key '%s'. You should also "
|
||||
"re-encipher the volume key of those volumes "
|
||||
@@ -2846,7 +2884,7 @@ int keystore_copy_key(struct keystore *keystore, const char *name,
|
||||
const char *newname, const char *volumes)
|
||||
{
|
||||
struct volume_check vol_check = { .keystore = keystore,
|
||||
.name = newname };
|
||||
.name = newname, .set = 0 };
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
struct key_filenames new_names = { NULL, NULL, NULL };
|
||||
struct properties *key_prop = NULL;
|
||||
@@ -3255,6 +3293,13 @@ static int _keystore_execute_cmd(const char *cmd,
|
||||
|
||||
struct crypt_info {
|
||||
bool execute;
|
||||
bool batch_mode;
|
||||
const char *keyfile;
|
||||
size_t keyfile_offset;
|
||||
size_t keyfile_size;
|
||||
size_t tries;
|
||||
bool open;
|
||||
bool format;
|
||||
char **volume_filter;
|
||||
int (*process_func)(struct keystore *keystore,
|
||||
const char *volume,
|
||||
@@ -3293,16 +3338,46 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
|
||||
const char *volume_type,
|
||||
struct crypt_info *info)
|
||||
{
|
||||
char *keyfile_opt = NULL, *offset_opt = NULL;
|
||||
char *size_opt = NULL, *tries_opt = NULL;
|
||||
char *common_passphrase_options;
|
||||
size_t common_len;
|
||||
char temp[100];
|
||||
int rc = 0;
|
||||
char *cmd;
|
||||
|
||||
sprintf(temp, "--sector-size %lu ", sector_size);
|
||||
|
||||
if (info->keyfile) {
|
||||
util_asprintf(&keyfile_opt, "--key-file '%s' ", info->keyfile);
|
||||
if (info->keyfile_offset > 0)
|
||||
util_asprintf(&offset_opt, "--keyfile-offset %lu ",
|
||||
info->keyfile_offset);
|
||||
if (info->keyfile_size > 0)
|
||||
util_asprintf(&size_opt, "--keyfile-size %lu ",
|
||||
info->keyfile_size);
|
||||
}
|
||||
if (info->tries > 0)
|
||||
util_asprintf(&tries_opt, "--tries %lu ", info->tries);
|
||||
util_asprintf(&common_passphrase_options, "%s%s%s%s",
|
||||
keyfile_opt != NULL ? keyfile_opt : "",
|
||||
offset_opt != NULL ? offset_opt : "",
|
||||
size_opt != NULL ? size_opt : "",
|
||||
tries_opt != NULL ? tries_opt : "");
|
||||
common_len = strlen(common_passphrase_options);
|
||||
free(keyfile_opt);
|
||||
free(offset_opt);
|
||||
free(size_opt);
|
||||
free(tries_opt);
|
||||
|
||||
if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) {
|
||||
if (info->format)
|
||||
return 0;
|
||||
|
||||
util_asprintf(&cmd,
|
||||
"cryptsetup plainOpen %s--key-file '%s' "
|
||||
"cryptsetup plainOpen %s%s--key-file '%s' "
|
||||
"--key-size %lu --cipher %s %s%s %s",
|
||||
info->batch_mode ? "-q " : "",
|
||||
keystore->verbose ? "-v " : "", key_file_name,
|
||||
key_file_size * 8, cipher_spec,
|
||||
sector_size > 0 ? temp : "", volume, dmname);
|
||||
@@ -3314,39 +3389,69 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
|
||||
printf("%s\n", cmd);
|
||||
}
|
||||
} else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) {
|
||||
util_asprintf(&cmd,
|
||||
"cryptsetup luksFormat %s--type luks2 "
|
||||
"--master-key-file '%s' --key-size %lu "
|
||||
"--cipher %s %s%s",
|
||||
keystore->verbose ? "-v " : "", key_file_name,
|
||||
key_file_size * 8, cipher_spec,
|
||||
sector_size > 0 ? temp : "", volume);
|
||||
if (info->open) {
|
||||
util_asprintf(&cmd,
|
||||
"cryptsetup luksOpen %s%s%s%s %s",
|
||||
info->batch_mode ? "-q " : "",
|
||||
keystore->verbose ? "-v " : "",
|
||||
common_len > 0 ?
|
||||
common_passphrase_options : "",
|
||||
volume, dmname);
|
||||
|
||||
if (info->execute) {
|
||||
printf("Executing: %s\n", cmd);
|
||||
rc = _keystore_execute_cmd(cmd, "cryptsetup");
|
||||
if (info->execute) {
|
||||
printf("Executing: %s\n", cmd);
|
||||
rc = _keystore_execute_cmd(cmd, "cryptsetup");
|
||||
} else {
|
||||
printf("%s\n", cmd);
|
||||
}
|
||||
} else {
|
||||
printf("%s\n", cmd);
|
||||
}
|
||||
/*
|
||||
* Use PBKDF2 as key derivation function for LUKS2
|
||||
* volumes. LUKS2 uses Argon2i as default, but this
|
||||
* might cause out-of-memory errors when multiple LUKS2
|
||||
* volumes are opened automatically via /etc/crypttab
|
||||
*/
|
||||
util_asprintf(&cmd,
|
||||
"cryptsetup luksFormat %s%s--type luks2 "
|
||||
"--master-key-file '%s' --key-size %lu "
|
||||
"--cipher %s --pbkdf pbkdf2 %s%s%s",
|
||||
info->batch_mode ? "-q " : "",
|
||||
keystore->verbose ? "-v " : "",
|
||||
key_file_name, key_file_size * 8,
|
||||
cipher_spec, common_len > 0 ?
|
||||
common_passphrase_options : "",
|
||||
sector_size > 0 ? temp : "", volume);
|
||||
|
||||
free(cmd);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
if (info->execute) {
|
||||
printf("Executing: %s\n", cmd);
|
||||
rc = _keystore_execute_cmd(cmd, "cryptsetup");
|
||||
} else {
|
||||
printf("%s\n", cmd);
|
||||
}
|
||||
|
||||
util_asprintf(&cmd,
|
||||
"zkey-cryptsetup setvp %s%s", volume,
|
||||
keystore->verbose ? " -V " : "");
|
||||
free(cmd);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
if (info->execute) {
|
||||
printf("Executing: %s\n", cmd);
|
||||
rc = _keystore_execute_cmd(cmd, "zkey-cryptsetup");
|
||||
} else {
|
||||
printf("%s\n", cmd);
|
||||
util_asprintf(&cmd,
|
||||
"zkey-cryptsetup setvp %s %s%s", volume,
|
||||
common_len > 0 ?
|
||||
common_passphrase_options : "",
|
||||
keystore->verbose ? "-V" : "");
|
||||
|
||||
if (info->execute) {
|
||||
printf("Executing: %s\n", cmd);
|
||||
rc = _keystore_execute_cmd(cmd,
|
||||
"zkey-cryptsetup");
|
||||
} else {
|
||||
printf("%s\n", cmd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(common_passphrase_options);
|
||||
free(cmd);
|
||||
return rc;
|
||||
}
|
||||
@@ -3376,7 +3481,7 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore),
|
||||
size_t key_file_size,
|
||||
size_t sector_size,
|
||||
const char *volume_type,
|
||||
struct crypt_info *UNUSED(info))
|
||||
struct crypt_info *info)
|
||||
{
|
||||
char temp[1000];
|
||||
|
||||
@@ -3395,11 +3500,22 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore),
|
||||
}
|
||||
|
||||
sprintf(temp, ",sector-size=%lu", sector_size);
|
||||
printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n",
|
||||
printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu%s\n",
|
||||
dmname, volume, key_file_name, cipher_spec,
|
||||
key_file_size * 8, sector_size > 0 ? temp : "");
|
||||
} else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) {
|
||||
printf("%s\t%s\n", dmname, volume);
|
||||
printf("%s\t%s\t%s\tluks", dmname, volume,
|
||||
info->keyfile != NULL ? info->keyfile : "none");
|
||||
if (info->keyfile != NULL) {
|
||||
if (info->keyfile_offset > 0)
|
||||
printf(",keyfile-offset=%lu",
|
||||
info->keyfile_offset);
|
||||
if (info->keyfile_size > 0)
|
||||
printf(",keyfile-size=%lu", info->keyfile_size);
|
||||
}
|
||||
if (info->tries > 0)
|
||||
printf(",tries=%lu", info->tries);
|
||||
printf("\n");
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -3584,11 +3700,21 @@ out:
|
||||
* @param[in] execute If TRUE the cryptsetup command is executed,
|
||||
* otherwise it is printed to stdout
|
||||
* @param[in] volume_type the type of volume to generate cryptsetup cmds for
|
||||
* *
|
||||
* @param[in] keyfile If non-NULL, specifies the name of the file to
|
||||
* read the passphrase from.
|
||||
* @param[in] keyfile_offset the offset in bytes for reading from keyfile
|
||||
* @param[in] keyfile_size the size in bytes for reading from keyfile
|
||||
* @param[in] tries the number of tries for passphrase entry
|
||||
* @param[in] batch_mode If TRUE, suppress cryptsetup confirmation questions
|
||||
* @param[in] open If TRUE, generate luksOpen/plainOpen commands
|
||||
* @param[in] format If TRUE, generate luksFormat commands
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
*/
|
||||
int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
|
||||
bool execute, const char *volume_type)
|
||||
bool execute, const char *volume_type,
|
||||
const char *keyfile, size_t keyfile_offset,
|
||||
size_t keyfile_size, size_t tries, bool batch_mode,
|
||||
bool open, bool format)
|
||||
{
|
||||
struct crypt_info info = { 0 };
|
||||
int rc;
|
||||
@@ -3605,6 +3731,13 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
|
||||
}
|
||||
|
||||
info.execute = execute;
|
||||
info.open = open;
|
||||
info.format = format;
|
||||
info.batch_mode = batch_mode;
|
||||
info.keyfile = keyfile;
|
||||
info.keyfile_offset = keyfile_offset;
|
||||
info.keyfile_size = keyfile_size;
|
||||
info.tries = tries;
|
||||
info.volume_filter = str_list_split(volume_filter);
|
||||
info.process_func = _keystore_process_cryptsetup;
|
||||
|
||||
@@ -3636,11 +3769,17 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
|
||||
* for the volume filter. If not specified, the filter
|
||||
* checks the volume part only.
|
||||
* @param[in] volume_type the type of volume to generate crypttab entries for
|
||||
* @param[in] keyfile If non-NULL, specifies the name of the file to
|
||||
* read the passphrase from.
|
||||
* @param[in] keyfile_offset the offset in bytes for reading from keyfile
|
||||
* @param[in] keyfile_size the size in bytes for reading from keyfile
|
||||
* @param[in] tries the number of tries for passphrase entry
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
*/
|
||||
int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
|
||||
const char *volume_type)
|
||||
const char *volume_type, const char *keyfile,
|
||||
size_t keyfile_offset, size_t keyfile_size, size_t tries)
|
||||
{
|
||||
struct crypt_info info = { 0 };
|
||||
int rc;
|
||||
@@ -3656,6 +3795,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info.keyfile = keyfile;
|
||||
info.keyfile_offset = keyfile_offset;
|
||||
info.keyfile_size = keyfile_size;
|
||||
info.tries = tries;
|
||||
info.volume_filter = str_list_split(volume_filter);
|
||||
info.process_func = _keystore_process_crypttab;
|
||||
|
||||
|
||||
@@ -28,25 +28,27 @@ struct keystore *keystore_new(const char *directory, bool verbose);
|
||||
|
||||
int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, size_t sector_size,
|
||||
size_t keybits, bool xts, const char *clear_key_file,
|
||||
const char *volume_type, int pkey_fd);
|
||||
const char *apqns, bool noapqncheck,
|
||||
size_t sector_size, size_t keybits, bool xts,
|
||||
const char *clear_key_file, const char *volume_type,
|
||||
int pkey_fd);
|
||||
|
||||
int keystore_import_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, size_t sector_size,
|
||||
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||
const char *import_file, const char *volume_type);
|
||||
|
||||
int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, long int sector_size,
|
||||
const char *volume_type);
|
||||
const char *apqns, bool noapqncheck,
|
||||
long int sector_size, const char *volume_type);
|
||||
|
||||
int keystore_rename_key(struct keystore *keystore, const char *name,
|
||||
const char *newname);
|
||||
|
||||
int keystore_validate_key(struct keystore *keystore, const char *name_filter,
|
||||
const char *apqn_filter, int pkey_fd);
|
||||
const char *apqn_filter, bool noapqncheck,
|
||||
int pkey_fd);
|
||||
|
||||
int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
|
||||
const char *apqn_filter,
|
||||
@@ -68,10 +70,14 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter,
|
||||
const char *volume_type);
|
||||
|
||||
int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
|
||||
bool execute, const char *volume_type);
|
||||
bool execute, const char *volume_type,
|
||||
const char *keyfile, size_t keyfile_offset,
|
||||
size_t keyfile_size, size_t tries, bool batch_mode,
|
||||
bool open, bool format);
|
||||
|
||||
int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
|
||||
const char *volume_type);
|
||||
const char *volume_type, const char *keyfile,
|
||||
size_t keyfile_offset, size_t keyfile_size, size_t tries);
|
||||
|
||||
void keystore_free(struct keystore *keystore);
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ behave in the same way as with \fBcryptsetup\fP.
|
||||
.IR bytes ]
|
||||
.RB [ \-\-tries | \-T
|
||||
.IR number ]
|
||||
.RB [ \-\-batch\-mode | \-q ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.RB [ \-\-debug | \-D ]
|
||||
.PP
|
||||
@@ -180,6 +181,20 @@ and
|
||||
to control which part of the key file is used as passphrase. These options
|
||||
behave in the same way as with \fBcryptsetup\fP.
|
||||
.PP
|
||||
The
|
||||
.B reencipher
|
||||
command creates a new key slot with the re-enciphered secure AES volume key.
|
||||
The new key slot uses
|
||||
.B PBKDF2
|
||||
as password based key derivation function. LUKS2 volumes typically default to
|
||||
.B Argon2i
|
||||
as password based key derivation function,
|
||||
but this might cause out-of-memory errors when multiple encrypted volumes are
|
||||
unlocked automatically at boot through /etc/crypttab. Because PAES
|
||||
uses secure AES keys as volume keys, the security of the key derivation
|
||||
function used to encrypt the volume key in the LUKS key slots is of less
|
||||
relevance.
|
||||
.PP
|
||||
.B Note:
|
||||
The \fBreencipher\fP command requires the CCA host library (libcsulcca.so)
|
||||
to be installed. For the supported environments and downloads, see:
|
||||
@@ -247,6 +262,7 @@ behave in the same way as with \fBcryptsetup\fP.
|
||||
.IR bytes ]
|
||||
.RB [ \-\-tries | \-T
|
||||
.IR number ]
|
||||
.RB [ \-\-batch\-mode | \-q ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.RB [ \-\-debug | \-D ]
|
||||
.PP
|
||||
@@ -288,6 +304,20 @@ and
|
||||
.B \-\-keyfile\-size
|
||||
to control which part of the key file is used as passphrase. These options
|
||||
behave in the same way the same as with \fBcryptsetup\fP.
|
||||
.PP
|
||||
The
|
||||
.B setkey
|
||||
command creates a new key slot with the re-enciphered secure AES volume key.
|
||||
The new key slot uses
|
||||
.B PBKDF2
|
||||
as password based key derivation function. LUKS2 volumes typically default to
|
||||
.B Argon2i
|
||||
as password based key derivation function,
|
||||
but this might cause out-of-memory errors when multiple encrypted volumes are
|
||||
unlocked automatically at boot through /etc/crypttab. Because PAES
|
||||
uses secure AES keys as volume keys, the security of the key derivation
|
||||
function used to encrypt the volume key in the LUKS key slots is of less
|
||||
relevance.
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -319,6 +349,9 @@ has been set (made active). When completing the staged re-enciphering, the
|
||||
(unbound) key slot containing the re-enciphered secure volume key becomes
|
||||
the active key slot and, optionally, all key slots containing the old secure
|
||||
volume key are removed.
|
||||
.TP
|
||||
.BR \-q ", " \-\-batch\-mode
|
||||
Suppresses all confirmation questions. Use with care!
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -327,6 +360,9 @@ volume key are removed.
|
||||
.BR \-m ", " \-\-master\-key\-file\~\fIfile\-name\fP
|
||||
Specifies the name of a file containing the secure AES key that is set as the
|
||||
new volume key.
|
||||
.TP
|
||||
.BR \-q ", " \-\-batch\-mode
|
||||
Suppresses all confirmation questions. Use with care!
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
#include "misc.h"
|
||||
#include "pkey.h"
|
||||
|
||||
/* Detect if cryptsetup 2.1 or later is available */
|
||||
#ifdef CRYPT_LOG_DEBUG_JSON
|
||||
#define HAVE_CRYPT_KEYSLOT_GET_PBKDF
|
||||
#endif
|
||||
|
||||
#define MAX_KEY_SIZE (8 * 1024 * 1024)
|
||||
#define MAX_PASSWORD_SIZE 512
|
||||
#define KEYFILE_BUFLEN 4096
|
||||
@@ -93,6 +98,7 @@ static struct zkey_cryptsetup_globals {
|
||||
bool inplace;
|
||||
bool staged;
|
||||
char *master_key_file;
|
||||
bool batch_mode;
|
||||
bool debug;
|
||||
bool verbose;
|
||||
void *lib_csulcca;
|
||||
@@ -176,6 +182,11 @@ static struct util_opt opt_vec[] = {
|
||||
.command = COMMAND_REENCIPHER,
|
||||
},
|
||||
OPT_PASSPHRASE_ENTRY(COMMAND_REENCIPHER),
|
||||
{
|
||||
.option = {"batch-mode", 0, NULL, 'q'},
|
||||
.desc = "Suppresses all confirmation questions. Use with care!",
|
||||
.command = COMMAND_REENCIPHER,
|
||||
},
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -204,6 +215,11 @@ static struct util_opt opt_vec[] = {
|
||||
.command = COMMAND_SETKEY,
|
||||
},
|
||||
OPT_PASSPHRASE_ENTRY(COMMAND_SETKEY),
|
||||
{
|
||||
.option = {"batch-mode", 0, NULL, 'q'},
|
||||
.desc = "Suppresses all confirmation questions. Use with care!",
|
||||
.command = COMMAND_SETKEY,
|
||||
},
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -414,8 +430,13 @@ static void cryptsetup_log(int level, const char *msg,
|
||||
fprintf(stderr, "%s: %s", program_invocation_short_name, msg);
|
||||
break;
|
||||
case CRYPT_LOG_DEBUG:
|
||||
fprintf(stderr, "%s: # %s", program_invocation_short_name, msg);
|
||||
fprintf(stderr, "%s: # %s\n", program_invocation_short_name, msg);
|
||||
break;
|
||||
#ifdef CRYPT_DEBUG_JSON
|
||||
case CRYPT_DEBUG_JSON:
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
warnx("Internal error on logging class for msg: %s", msg);
|
||||
break;
|
||||
@@ -1096,7 +1117,11 @@ static int put_vp_token(struct crypt_device *cd, int token,
|
||||
*/
|
||||
static int open_device(const char *device, struct crypt_device **cd)
|
||||
{
|
||||
const struct crypt_pbkdf_type *pbkdf;
|
||||
const struct crypt_pbkdf_type pbkdf2 = {
|
||||
.type = CRYPT_KDF_PBKDF2,
|
||||
.hash = "sha256",
|
||||
.time_ms = 2000,
|
||||
};
|
||||
struct crypt_device *cdev = NULL;
|
||||
int rc;
|
||||
|
||||
@@ -1128,10 +1153,14 @@ static int open_device(const char *device, struct crypt_device **cd)
|
||||
goto out;
|
||||
}
|
||||
|
||||
pbkdf = crypt_get_pbkdf_type(cdev);
|
||||
rc = crypt_set_pbkdf_type(cdev, pbkdf);
|
||||
/*
|
||||
* Set PBKDF2 as default key derivation function. LUKS2 uses
|
||||
* Argon2i as default, but this might cause out-of-memory errors when
|
||||
* multiple LUKS2 volumes are opened automatically via /etc/crypttab
|
||||
*/
|
||||
rc = crypt_set_pbkdf_type(cdev, &pbkdf2);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to set the PBKDF-type for device '%s': %s",
|
||||
warnx("Failed to set the PBKDF for device '%s': %s",
|
||||
device, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
@@ -1155,6 +1184,12 @@ static bool prompt_for_yes(void)
|
||||
{
|
||||
char str[20];
|
||||
|
||||
if (g.batch_mode) {
|
||||
printf("(yes implied because '--batch-mode' | '-q' option is "
|
||||
"specified)\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fgets(str, sizeof(str), stdin) == NULL)
|
||||
return false;
|
||||
|
||||
@@ -1311,6 +1346,9 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
|
||||
char **password, size_t *password_len,
|
||||
const char *prompt)
|
||||
{
|
||||
#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
|
||||
struct crypt_pbkdf_type pbkdf;
|
||||
#endif
|
||||
char *vkey = NULL;
|
||||
char *pw = NULL;
|
||||
long long tries;
|
||||
@@ -1362,6 +1400,30 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
|
||||
keyslot = rc;
|
||||
pr_verbose("Volume key obtained from key slot %d", keyslot);
|
||||
|
||||
#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
|
||||
/*
|
||||
* Get PBKDF of the key slot that was opened, and use its PBKDF for
|
||||
* new key slots.
|
||||
*/
|
||||
memset(&pbkdf, 0, sizeof(pbkdf));
|
||||
rc = crypt_keyslot_get_pbkdf(g.cd, keyslot, &pbkdf);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to get the PBKDF for key slot %d: %s",
|
||||
keyslot, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Reuse already benchmarked number of iterations */
|
||||
pbkdf.flags |= CRYPT_PBKDF_NO_BENCHMARK;
|
||||
|
||||
rc = crypt_set_pbkdf_type(g.cd, &pbkdf);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to set the PBKDF for new key slots: %s",
|
||||
strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (key != NULL)
|
||||
*key = vkey;
|
||||
else
|
||||
@@ -2191,6 +2253,9 @@ int main(int argc, char *argv[])
|
||||
case 'm':
|
||||
g.master_key_file = optarg;
|
||||
break;
|
||||
case 'q':
|
||||
g.batch_mode = true;
|
||||
break;
|
||||
case 'D':
|
||||
g.debug = true;
|
||||
g.verbose = true;
|
||||
@@ -2238,7 +2303,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
crypt_set_log_callback(NULL, cryptsetup_log, NULL);
|
||||
if (g.debug)
|
||||
crypt_set_debug_level(-1);
|
||||
#ifdef CRYPT_DEBUG_JSON
|
||||
crypt_set_debug_level(CRYPT_DEBUG_JSON);
|
||||
#else
|
||||
crypt_set_debug_level(CRYPT_DEBUG_ALL);
|
||||
#endif
|
||||
|
||||
if (command->open_device) {
|
||||
if (g.pos_arg == NULL) {
|
||||
|
||||
244
zkey/zkey.1
244
zkey/zkey.1
@@ -92,6 +92,7 @@ key repository.
|
||||
.IR volume1:dmname1[,volume2:dmname2[,...]] ]
|
||||
.RB [ \-\-apqns | \-a
|
||||
.IR card1.domain1[,card2.domain2[,...]] ]
|
||||
.RB [ \-\-no\-apqn\-check ]
|
||||
.RB [ \-\-sector-size | \-S
|
||||
.IR bytes ]
|
||||
.RB [ \-\-volume-type | \-t
|
||||
@@ -141,6 +142,9 @@ options.
|
||||
.BR validate | val
|
||||
.RB [ \-\-name | \-N
|
||||
.IR key-name ]
|
||||
.RB [ \-\-apqns | \-a
|
||||
.IR card1.domain1[,card2.domain2[,...]] ]
|
||||
.RB [ \-\-no\-apqn\-check ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.PP
|
||||
Use the
|
||||
@@ -160,8 +164,21 @@ contained in the secure key repository, specify the name of the key
|
||||
or a pattern containing wildcards using the
|
||||
.B \-\-name
|
||||
option. When wildcards are used you must quote the value.
|
||||
If neither option \fIsecure\-key\-file\fP nor option
|
||||
You can also specify the
|
||||
.B \-\-apqns
|
||||
option to validate those secure keys which are associated with the specified
|
||||
cryptographic adapters (APQNs). You can use wildcards for the APQN
|
||||
specification. When wildcards are used you must quote the value.
|
||||
If both option
|
||||
.B \-\-name
|
||||
and option
|
||||
.B \-\-apqns
|
||||
are specified then all secure keys contained in the key repository that match
|
||||
both patterns are validated.
|
||||
If neither option \fIsecure\-key\-file\fP nor options
|
||||
.B \-\-name
|
||||
or
|
||||
.B \-\-apqns
|
||||
are specified, then all secure keys contained in the key repository
|
||||
are validated.
|
||||
.
|
||||
@@ -259,7 +276,7 @@ and option
|
||||
.B \-\-apqns
|
||||
are specified then all secure keys
|
||||
contained in the key repository that match both patterns are re-enciphered.
|
||||
If all both options are omitted, then all secure keys contained in the key
|
||||
If both options are omitted, then all secure keys contained in the key
|
||||
repository are re-enciphered.
|
||||
.PP
|
||||
Re-enciphering a secure key contained in the secure key repository can be
|
||||
@@ -298,6 +315,7 @@ to be installed. For the supported environments and downloads, see:
|
||||
.IR volume1:dmname1[,volume2:dmname2[,...]] ]
|
||||
.RB [ \-\-apqns | \-a
|
||||
.IR card1.domain1[,card2.domain2[,...]] ]
|
||||
.RB [ \-\-no\-apqn\-check ]
|
||||
.RB [ \-\-sector-size | \-S
|
||||
.IR bytes ]
|
||||
.RB [ \-\-volume-type | \-t
|
||||
@@ -410,6 +428,7 @@ because the secure key is contained in the LUKS2 header.
|
||||
.IR [+|-]volume1:dmname1[,volume2:dmname2[,...]] ]
|
||||
.RB [ \-\-apqns | \-a
|
||||
.IR [+|-]card1.domain1[,card2.domain2[,...]] ]
|
||||
.RB [ \-\-no\-apqn\-check ]
|
||||
.RB [ \-\-sector-size | \-S
|
||||
.IR bytes ]
|
||||
.RB [ \-\-volume-type | \-t
|
||||
@@ -487,8 +506,8 @@ secure key is contained in the LUKS2 header.
|
||||
.B copy | co
|
||||
.RB \-\-name | \-N
|
||||
.IR key-name
|
||||
.B \-\-new-key-name | \-w
|
||||
.IR new-name
|
||||
.B \-\-new\-name | \-w
|
||||
.IR new-key-name
|
||||
.RB [ \-\-volumes | \-l
|
||||
.IR volume1:dmname1[,volume2:dmname2[,...]] ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
@@ -519,6 +538,14 @@ volumes afterwards.
|
||||
.IR volume1[:dmname1][,volume2[:dmname2][,...]] ]
|
||||
.RB [ \-\-volume-type | \-t
|
||||
.IR type ]
|
||||
.RB [ \-\-key\-file
|
||||
.IR file-name ]
|
||||
.RB [ \-\-keyfile\-offset
|
||||
.IR bytes ]
|
||||
.RB [ \-\-keyfile\-size
|
||||
.IR bytes ]
|
||||
.RB [ \-\-tries
|
||||
.IR number ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.
|
||||
.PP
|
||||
@@ -537,6 +564,23 @@ name are selected.
|
||||
Specify the
|
||||
.B \-\-volume-type
|
||||
option to generate crypttab entries for the specified volume type only.
|
||||
.P
|
||||
For LUKS2 volumes, a passphrase is required. You are prompted for the
|
||||
passphrase during system startup when crypttab is evaluated, unless option
|
||||
.B \-\-key\-file
|
||||
is specified. Option
|
||||
.B \-\-tries
|
||||
specifies how often a passphrase can be re-entered. When option
|
||||
.B \-\-key\-file
|
||||
is specified, the passphrase is read from the specified file. You can specify
|
||||
options
|
||||
.B \-\-keyfile\-offset
|
||||
and
|
||||
.B \-\-keyfile\-size
|
||||
to control which part of the key file is used as passphrase. These options are
|
||||
passed to the generated crypttab entries and are only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.
|
||||
.SS "Generate cryptsetup commands for volumes associated with secure AES keys"
|
||||
.
|
||||
@@ -547,14 +591,24 @@ option to generate crypttab entries for the specified volume type only.
|
||||
.RB [ \-\-volume-type | \-t
|
||||
.IR type ]
|
||||
.RB [ \-\-run | \-r ]
|
||||
.RB [ \-\-open ]
|
||||
.RB [ \-\-format ]
|
||||
.RB [ \-\-key\-file
|
||||
.IR file-name ]
|
||||
.RB [ \-\-keyfile\-offset
|
||||
.IR bytes ]
|
||||
.RB [ \-\-keyfile\-size
|
||||
.IR bytes ]
|
||||
.RB [ \-\-tries
|
||||
.IR number ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.
|
||||
.PP
|
||||
Use the
|
||||
.B cryptsetup
|
||||
command to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksFormat\fP
|
||||
commands for volumes that are associated with secure keys contained in the
|
||||
secure key repository. Specify the
|
||||
command to generate \fBcryptsetup plainOpen\fP, \fBcryptsetup luksOpen\fP, or
|
||||
\fBcryptsetup luksFormat\fP commands for volumes that are associated with
|
||||
secure keys contained in the secure key repository. Specify the
|
||||
.B \-\-volumes
|
||||
option to limit the list
|
||||
of volumes where cryptsetup commands are generated for. You can use wildcards.
|
||||
@@ -566,7 +620,44 @@ name are selected. Specify the
|
||||
option to generate cryptsetup commands for the specified volume type only.
|
||||
Specify the
|
||||
.B \-\-run
|
||||
option to run the generated cryptsetup commands.
|
||||
option to run the generated cryptsetup commands. Specify the
|
||||
.B \-\-open
|
||||
to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksOpen\fP commands.
|
||||
For the plain volume type, this is the default. Specify the
|
||||
.B \-\-format
|
||||
option to generate \fBcryptsetup luksFormat\fP commands. For the LUKS2 volume
|
||||
type, this is the default. If specified for the plain volume type, then no
|
||||
command is generated.
|
||||
.P
|
||||
For LUKS2 volumes, the generated \fBcryptsetup luksFormat\fP contains
|
||||
option \fB\-\-pbkdf pbkdf2\fP to set \fBPBKDF2\fP as password based key
|
||||
derivation function. LUKS2 volumes typically default to \fBArgon2i\fP as
|
||||
password based key derivation function, but this might cause out-of-memory
|
||||
errors when multiple encrypted volumes are unlocked automatically at boot
|
||||
through /etc/crypttab. Because PAES uses secure AES keys as volume keys, the
|
||||
security of the key derivation function used to encrypt the volume key in the
|
||||
LUKS key slots is of less relevance.
|
||||
.P
|
||||
For LUKS2 volumes, a passphrase is required. You are prompted for the
|
||||
passphrase when running the generated commands, unless option
|
||||
.B \-\-key\-file
|
||||
is specified. Option
|
||||
.B \-\-tries
|
||||
specifies how often a passphrase can be re-entered. When option
|
||||
.B \-\-key\-file
|
||||
is specified, the passphrase is read from the specified file. You can specify
|
||||
options
|
||||
.B \-\-keyfile\-offset
|
||||
and
|
||||
.B \-\-keyfile\-size
|
||||
to control which part of the key file is used as passphrase. These options are
|
||||
only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled. To avoid cryptsetup confirmation
|
||||
questions, you can specify the
|
||||
.B \-\-batch\-mode
|
||||
option. These options are passed to the generated command(s) and behave in the
|
||||
same way as with \fBcryptsetup\fP.
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -613,8 +704,14 @@ Specifies a comma-separated list of cryptographic adapters in CCA
|
||||
coprocessor mode (APQN) which are associated with the secure AES key in the
|
||||
repository. Each APQN association specifies a card and domain number separated
|
||||
by a period (like lszcrypt displays it). When at least one APQN is specified,
|
||||
then the first one is used to generate the key. If no APQNs are specified,
|
||||
then an APQN is selected automatically. All specified APQNs must be online.
|
||||
then the first online APQN is used to generate the key. If no APQNs are
|
||||
specified, then an APQN is selected automatically. All specified APQNs must be
|
||||
online, unless the \fB\-\-no\-apqn\-check\fP option is specified.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-\-no\-apqn\-check
|
||||
Do not check if the specified APQNs are available. Use this option to
|
||||
associate APQNs with a secure AES key that are currently not available.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-S ", " \-\-sector-size\~\fIbytes\fP
|
||||
@@ -641,6 +738,19 @@ Specifies the name of the secure key in the secure key repository. You can
|
||||
use wildcards to select multiple secure keys in the secure key repository.
|
||||
When wildcards are used you must quote the value.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP
|
||||
Specifies a comma-separated list of cryptographic adapters in CCA
|
||||
coprocessor mode (APQNs). You can use wildcards in the APQN specification.
|
||||
All secure keys contained in the secure key repository
|
||||
which are associated with the specified APQNs are validated.
|
||||
Each APQN specifies a card and domain number separated by a period (like
|
||||
lszcrypt displays it).
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-\-no\-apqn\-check
|
||||
Do not check if the associated APQNs are available.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -724,7 +834,13 @@ This option is only used for secure keys contained in the secure key repository.
|
||||
Specifies a comma-separated list of cryptographic adapters in CCA
|
||||
coprocessor mode (APQN) which are associated with the secure AES key in the
|
||||
repository. Each APQN association specifies a card and domain number separated
|
||||
by a period (like lszcrypt displays it). All specified APQNs must be online.
|
||||
by a period (like lszcrypt displays it). All specified APQNs must be online,
|
||||
unless option \fB\-\-no\-apqn\-check\fP is specified.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-\-no\-apqn\-check
|
||||
Do not check if the specified APQNs are available. Use this option to
|
||||
associate APQNs with a secure AES key that are currently not available.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-S ", " \-\-sector-size\~\fIbytes\fP
|
||||
@@ -843,7 +959,13 @@ To remove an APQN from the associated APQNs, prefix the APQN with a \fI-\fP.
|
||||
To set (replace) the APQN association do not specify a prefix.
|
||||
You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or
|
||||
remove (or set) the associations with one command.
|
||||
All APQNs being added or set (replaced) must be online.
|
||||
All APQNs being added or set (replaced) must be online, unless option
|
||||
\fB\-\-no\-apqn\-check\fP is specified.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-\-no\-apqn\-check
|
||||
Do not check if the specified APQNs are available. Use this option to
|
||||
associate APQNs with a secure AES key that are currently not available.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-S ", " \-\-sector-size\~\fIbytes\fP
|
||||
@@ -918,6 +1040,46 @@ This option is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-\-key\-file\~\fIfile\-name\fP
|
||||
Reads the passphrase from the specified file. If this option is omitted, then
|
||||
you are prompted to enter the passphrase interactively during system startup.
|
||||
This option is passed to the generated crypttab entries for LUKS2 volumes, and
|
||||
is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-\-keyfile\-offset\~\fIbytes\fP
|
||||
Specifies the number of bytes to skip before starting to read in the file
|
||||
specified with option \fB\-\-key\-file\fP. If omitted, the file is read
|
||||
from the beginning. When option \fB\-\-key\-file\fP is not specified, this
|
||||
option is ignored. This option is passed to the generated crypttab entries
|
||||
for LUKS2 volumes, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled. Not all distributions support the
|
||||
.B keyfile-offset
|
||||
option in crypttab entries.
|
||||
.TP
|
||||
.BR \-\-keyfile\-size\~\fIbytes\fP
|
||||
Specifies the number of bytes to be read from the beginning of the file
|
||||
specified with option \fB\-\-key\-file\fP. If omitted, the file is read
|
||||
until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts
|
||||
at the offset. When option \fB\-\-key\-file\fP is not specified, this option is
|
||||
ignored. This option is passed to the generated crypttab entries for LUKS2
|
||||
volumes, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled. Not all distributions support the
|
||||
.B keyfile-size
|
||||
option in crypttab entries.
|
||||
.TP
|
||||
.BR \-\-tries\~\fInumber\fP
|
||||
Specifies how often the interactive input of the passphrase can be re-entered
|
||||
during system startup. The default is 3 times. When option \fB\-\-key\-file\fP
|
||||
is specified, this option is ignored, and the passphrase is read only once from
|
||||
the file. This option is passed to the generated crypttab entries for LUKS2
|
||||
volumes, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.
|
||||
.
|
||||
.
|
||||
@@ -947,6 +1109,64 @@ This option is only used for secure keys contained in the secure key repository.
|
||||
Runs the generated cryptsetup commands. When one of the cryptsetup command fail,
|
||||
no further cryptsetup commands are run, and zkey ends with an error.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-\-open
|
||||
Generates \fBcryptsetup luksOpen\fP or \fBcryptsetup plainOpen\fP commands.
|
||||
For a plain volume type, this is the default. This option can not be specified
|
||||
together with the
|
||||
.BR \-\-format
|
||||
option, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-\-format
|
||||
Generates \fBcryptsetup luksFormat\fP commands. For a LUKS2 volume type, this
|
||||
is the default. If specified for a plain volume type, then no command is
|
||||
generated. This option can not be specified together with the
|
||||
.BR \-\-open
|
||||
option, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-\-key\-file\~\fIfile\-name\fP
|
||||
Reads the passphrase from the specified file. If this option is omitted,
|
||||
or if the file\-name is \fI-\fP (a dash), then you are prompted to enter the
|
||||
passphrase interactively. This option is passed to the generated command(s)
|
||||
for LUKS2 volumes, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-\-keyfile\-offset\~\fIbytes\fP
|
||||
Specifies the number of bytes to skip before starting to read in the file
|
||||
specified with option \fB\-\-key\-file\fP. If omitted, the file is read
|
||||
from the beginning. When option \fB\-\-key\-file\fP is not specified, this
|
||||
option is ignored. This option is passed to the generated command(s)
|
||||
for LUKS2 volumes, and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-\-keyfile\-size\~\fIbytes\fP
|
||||
Specifies the number of bytes to be read from the beginning of the file
|
||||
specified with option \fB\-\-key\-file\fP. If omitted, the file is read
|
||||
until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts
|
||||
at the offset. When option \fB\-\-key\-file\fP is not specified, this option is
|
||||
ignored. This option is passed to the generated command(s) for LUKS2 volumes,
|
||||
and is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-\-tries\~\fInumber\fP
|
||||
Specifies how often the interactive input of the passphrase can be re-entered.
|
||||
The default is 3 times. When option \fB\-\-key\-file\fP is specified, this
|
||||
option is ignored, and the passphrase is read only once from the file.
|
||||
This option is passed to the generated command(s) for LUKS2 volumes, and is
|
||||
only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-q ", " \-\-batch\-mode
|
||||
Suppress cryptsetup confirmation questions. This option is passed to the generated
|
||||
cryptsetup command(s).
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
282
zkey/zkey.c
282
zkey/zkey.c
@@ -67,11 +67,19 @@ static struct zkey_globals {
|
||||
char *description;
|
||||
char *volumes;
|
||||
char *apqns;
|
||||
bool noapqncheck;
|
||||
long int sector_size;
|
||||
char *volume_type;
|
||||
char *newname;
|
||||
bool run;
|
||||
bool batch_mode;
|
||||
char *keyfile;
|
||||
long long keyfile_offset;
|
||||
long long keyfile_size;
|
||||
long long tries;
|
||||
bool force;
|
||||
bool open;
|
||||
bool format;
|
||||
void *lib_csulcca;
|
||||
t_CSNBKTC dll_CSNBKTC;
|
||||
int pkey_fd;
|
||||
@@ -102,6 +110,14 @@ static struct zkey_globals {
|
||||
#define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY"
|
||||
#define DEFAULT_KEYSTORE "/etc/zkey/repository"
|
||||
|
||||
#define OPT_CRYPTSETUP_KEYFILE 256
|
||||
#define OPT_CRYPTSETUP_KEYFILE_OFFSET 257
|
||||
#define OPT_CRYPTSETUP_KEYFILE_SIZE 258
|
||||
#define OPT_CRYPTSETUP_TRIES 259
|
||||
#define OPT_CRYPTSETUP_OPEN 260
|
||||
#define OPT_CRYPTSETUP_FORMAT 261
|
||||
#define OPT_NO_APQN_CHECK 262
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
@@ -172,6 +188,14 @@ static struct util_opt opt_vec[] = {
|
||||
"repository",
|
||||
.command = COMMAND_GENERATE,
|
||||
},
|
||||
{
|
||||
.option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
|
||||
.desc = "Do not check if the specified APQN(s) are available. "
|
||||
"Use this option to associate APQN(s) with a secure "
|
||||
"AES key that are currently not available.",
|
||||
.command = COMMAND_GENERATE,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "sector-size", required_argument, NULL, 'S'},
|
||||
.argument = "bytes",
|
||||
@@ -281,6 +305,12 @@ static struct util_opt opt_vec[] = {
|
||||
"associated with specific crypto cards",
|
||||
.command = COMMAND_VALIDATE,
|
||||
},
|
||||
{
|
||||
.option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
|
||||
.desc = "Do not check if the associated APQN(s) are available",
|
||||
.command = COMMAND_VALIDATE,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -316,6 +346,14 @@ static struct util_opt opt_vec[] = {
|
||||
"repository",
|
||||
.command = COMMAND_IMPORT,
|
||||
},
|
||||
{
|
||||
.option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
|
||||
.desc = "Do not check if the specified APQN(s) are available. "
|
||||
"Use this option to associate APQN(s) with a secure "
|
||||
"AES key that are currently not available.",
|
||||
.command = COMMAND_IMPORT,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "sector-size", required_argument, NULL, 'S'},
|
||||
.argument = "512|4096",
|
||||
@@ -453,6 +491,14 @@ static struct util_opt opt_vec[] = {
|
||||
"specify '-CARD.DOMAIN[,...]'",
|
||||
.command = COMMAND_CHANGE,
|
||||
},
|
||||
{
|
||||
.option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
|
||||
.desc = "Do not check if the specified APQN(s) are available. "
|
||||
"Use this option to associate APQN(s) with a secure "
|
||||
"AES key that are currently not available.",
|
||||
.command = COMMAND_CHANGE,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "sector-size", required_argument, NULL, 'S'},
|
||||
.argument = "0|512|4096",
|
||||
@@ -547,6 +593,53 @@ static struct util_opt opt_vec[] = {
|
||||
"entry is to be generated",
|
||||
.command = COMMAND_CRYPTTAB,
|
||||
},
|
||||
{
|
||||
.option = {"key-file", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_KEYFILE},
|
||||
.argument = "FILE-NAME",
|
||||
.desc = "Read the passphrase from the specified file. "
|
||||
"The specified file is passed to the generated "
|
||||
"crypttab entry for LUKS2 volumes",
|
||||
.command = COMMAND_CRYPTTAB,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"keyfile-offset", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_KEYFILE_OFFSET},
|
||||
.argument = "BYTES",
|
||||
.desc = "Specifies the number of bytes to skip in the file "
|
||||
"specified with option '--key-file'. "
|
||||
"The specified offset is passed to the generated "
|
||||
"crypttab entry for LUKS2 volumes. Not all "
|
||||
"distributions support the 'keyfile-offset' option in "
|
||||
"crypttab entries",
|
||||
.command = COMMAND_CRYPTTAB,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"keyfile-size", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_KEYFILE_SIZE},
|
||||
.argument = "BYTES",
|
||||
.desc = "Specifies the number of bytes to read from the file "
|
||||
"specified with option '--key-file'. "
|
||||
"The specified size is passed to the generated "
|
||||
"crypttab entry for LUKS2 volumes. Not all "
|
||||
"distributions support the 'keyfile-size' option in "
|
||||
"crypttab entries",
|
||||
.command = COMMAND_CRYPTTAB,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"tries", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_TRIES},
|
||||
.argument = "NUMBER",
|
||||
.desc = "Specifies how often the interactive input of the "
|
||||
"passphrase can be retried. "
|
||||
"The specified number is passed to the generated "
|
||||
"crypttab entry for LUKS2 volumes",
|
||||
.command = COMMAND_CRYPTTAB,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
#endif
|
||||
/***********************************************************/
|
||||
{
|
||||
@@ -582,6 +675,75 @@ static struct util_opt opt_vec[] = {
|
||||
.desc = "Runs the generated cryptsetup command",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
},
|
||||
#ifdef HAVE_LUKS2_SUPPORT
|
||||
{
|
||||
.option = {"key-file", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_KEYFILE},
|
||||
.argument = "FILE-NAME",
|
||||
.desc = "Read the passphrase from the specified file. "
|
||||
"This option is passed to the generated command(s) for "
|
||||
"LUKS2 volumes",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"keyfile-offset", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_KEYFILE_OFFSET},
|
||||
.argument = "BYTES",
|
||||
.desc = "Specifies the number of bytes to skip in the file "
|
||||
"specified with option '--key-file'. "
|
||||
"This option is passed to the generated command(s) for "
|
||||
"LUKS2 volumes",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"keyfile-size", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_KEYFILE_SIZE},
|
||||
.argument = "BYTES",
|
||||
.desc = "Specifies the number of bytes to read from the file "
|
||||
"specified with option '--key-file'. "
|
||||
"This option is passed to the generated command(s) for "
|
||||
"LUKS2 volumes",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"tries", required_argument, NULL,
|
||||
OPT_CRYPTSETUP_TRIES},
|
||||
.argument = "NUMBER",
|
||||
.desc = "Specifies how often the interactive input of the "
|
||||
"passphrase can be retried. "
|
||||
"This option is passed to the generated command(s) for "
|
||||
"LUKS2 volumes",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.option = {"batch-mode", 0, NULL, 'q'},
|
||||
.desc = "Suppresses cryptsetup confirmation questions. "
|
||||
"This option is passed to the generated cryptsetup "
|
||||
"command(s)",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
},
|
||||
#ifdef HAVE_LUKS2_SUPPORT
|
||||
{
|
||||
.option = {"open", 0, NULL, OPT_CRYPTSETUP_OPEN},
|
||||
.desc = "Generates luksOpen or plainOpen commands. For the "
|
||||
"plain volume type, this is the default",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"format", 0, NULL, OPT_CRYPTSETUP_FORMAT},
|
||||
.desc = "Generates luksFormat commands. For the LUKS2 volume "
|
||||
"type, this is the default. If specified for the "
|
||||
"plain volume type, then no command is generated",
|
||||
.command = COMMAND_CRYPTSETUP,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
#endif
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -883,8 +1045,9 @@ static int command_generate_repository(void)
|
||||
g.sector_size = 0;
|
||||
|
||||
rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes,
|
||||
g.apqns, g.sector_size, g.keybits, g.xts,
|
||||
g.clearkeyfile, g.volume_type, g.pkey_fd);
|
||||
g.apqns, g.noapqncheck, g.sector_size,
|
||||
g.keybits, g.xts, g.clearkeyfile,
|
||||
g.volume_type, g.pkey_fd);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -903,6 +1066,12 @@ static int command_generate(void)
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.apqns == NULL && g.noapqncheck) {
|
||||
warnx("Option '--noapqncheck' is only valid together with "
|
||||
"the '--apqns|-a' option");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.name != NULL)
|
||||
return command_generate_repository();
|
||||
if (g.pos_arg != NULL) {
|
||||
@@ -918,6 +1087,12 @@ static int command_generate(void)
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.noapqncheck) {
|
||||
warnx("Option '--noapqncheck' is not valid for "
|
||||
"generating a key outside of the repository");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.description != NULL) {
|
||||
warnx("Option '--description|-d' is not valid for "
|
||||
"generating a key outside of the repository");
|
||||
@@ -1141,6 +1316,12 @@ static int command_validate_file(void)
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.noapqncheck) {
|
||||
warnx("Option '--noapqncheck' is not valid for "
|
||||
"validating a key outside of the repository");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Read the secure key to be re-enciphered */
|
||||
secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose);
|
||||
@@ -1194,7 +1375,15 @@ static int command_validate_repository(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.pkey_fd);
|
||||
if (g.apqns == NULL && g.noapqncheck) {
|
||||
warnx("Option '--noapqncheck' is only valid together with "
|
||||
"the '--apqns|-a' option");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck,
|
||||
g.pkey_fd);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1231,9 +1420,16 @@ static int command_import(void)
|
||||
if (g.sector_size < 0)
|
||||
g.sector_size = 0;
|
||||
|
||||
if (g.apqns == NULL && g.noapqncheck) {
|
||||
warnx("Option '--noapqncheck' is only valid together with "
|
||||
"the '--apqns|-a' option");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes,
|
||||
g.apqns, g.sector_size, g.pos_arg,
|
||||
g.volume_type);
|
||||
g.apqns, g.noapqncheck, g.sector_size,
|
||||
g.pos_arg, g.volume_type);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1304,9 +1500,16 @@ static int command_change(void)
|
||||
misc_print_required_parm("--name/-N");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.apqns == NULL && g.noapqncheck) {
|
||||
warnx("Option '--noapqncheck' is only valid together with "
|
||||
"the '--apqns|-a' option");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes,
|
||||
g.apqns, g.sector_size, g.volume_type);
|
||||
g.apqns, g.noapqncheck, g.sector_size,
|
||||
g.volume_type);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1366,7 +1569,8 @@ static int command_crypttab(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type);
|
||||
rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type, g.keyfile,
|
||||
g.keyfile_offset, g.keyfile_size, g.tries);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1380,7 +1584,16 @@ static int command_cryptsetup(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type);
|
||||
if (g.open && g.format) {
|
||||
warnx("Either '--open' or '--format' can be specified, but "
|
||||
"not both");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type,
|
||||
g.keyfile, g.keyfile_offset, g.keyfile_size,
|
||||
g.tries, g.batch_mode, g.open, g.format);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1546,6 +1759,9 @@ int main(int argc, char *argv[])
|
||||
case 'a':
|
||||
g.apqns = optarg;
|
||||
break;
|
||||
case OPT_NO_APQN_CHECK:
|
||||
g.noapqncheck = 1;
|
||||
break;
|
||||
case 'S':
|
||||
g.sector_size = strtol(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' ||
|
||||
@@ -1574,6 +1790,56 @@ int main(int argc, char *argv[])
|
||||
case 'V':
|
||||
g.verbose = 1;
|
||||
break;
|
||||
#ifdef HAVE_LUKS2_SUPPORT
|
||||
case OPT_CRYPTSETUP_KEYFILE:
|
||||
g.keyfile = optarg;
|
||||
break;
|
||||
case OPT_CRYPTSETUP_KEYFILE_OFFSET:
|
||||
g.keyfile_offset = strtoll(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' ||
|
||||
g.keyfile_offset < 0 ||
|
||||
(g.keyfile_offset == LLONG_MAX &&
|
||||
errno == ERANGE)) {
|
||||
warnx("Invalid value for '--keyfile-offset': "
|
||||
"'%s'", optarg);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case OPT_CRYPTSETUP_KEYFILE_SIZE:
|
||||
g.keyfile_size = strtoll(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' ||
|
||||
g.keyfile_size <= 0 ||
|
||||
(g.keyfile_size == LLONG_MAX && errno == ERANGE)) {
|
||||
warnx("Invalid value for '--keyfile-size': "
|
||||
"'%s'", optarg);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case OPT_CRYPTSETUP_TRIES:
|
||||
g.tries = strtoll(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' ||
|
||||
g.tries <= 0 ||
|
||||
(g.tries == LLONG_MAX && errno == ERANGE)) {
|
||||
warnx("Invalid value for '--tries': '%s'",
|
||||
optarg);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 'q':
|
||||
g.batch_mode = 1;
|
||||
break;
|
||||
#ifdef HAVE_LUKS2_SUPPORT
|
||||
case OPT_CRYPTSETUP_OPEN:
|
||||
g.open = 1;
|
||||
break;
|
||||
case OPT_CRYPTSETUP_FORMAT:
|
||||
g.format = 1;
|
||||
break;
|
||||
#endif
|
||||
case 'h':
|
||||
print_help(command);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "lib/util_proc.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_sys.h"
|
||||
|
||||
#include "zpcictl.h"
|
||||
|
||||
@@ -150,7 +151,12 @@ static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr)
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
fopen_err(path);
|
||||
fscanf(fp, "%x", &val);
|
||||
if (fscanf(fp, "%x", &val) != 1) {
|
||||
fclose(fp);
|
||||
warnx("Could not read file %s: %s", path, strerror(errno));
|
||||
free(path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fclose(fp);
|
||||
free(path);
|
||||
|
||||
@@ -180,45 +186,6 @@ static void sysfs_write_data(struct zpci_report_error *report, char *slot)
|
||||
free(path);
|
||||
}
|
||||
|
||||
/* lstat() doesn't work for sysfs files, so we have to work with a fixed size */
|
||||
#define READLINK_SIZE 256
|
||||
|
||||
static int sysfs_get_slot_addr(const char *dev, char *slot)
|
||||
{
|
||||
char device[READLINK_SIZE], *result;
|
||||
unsigned int major, minor;
|
||||
struct stat dev_stat;
|
||||
ssize_t len;
|
||||
char *path;
|
||||
|
||||
if (stat(dev, &dev_stat) != 0) {
|
||||
warnx("Could not get stat information for %s: %s",
|
||||
dev, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
major = major(dev_stat.st_rdev);
|
||||
minor = minor(dev_stat.st_rdev);
|
||||
|
||||
path = util_path_sysfs("dev/char/%u:%u/device", major, minor);
|
||||
len = readlink(path, device, READLINK_SIZE - 1);
|
||||
free(path);
|
||||
if (len != -1) {
|
||||
device[len] = '\0';
|
||||
} else {
|
||||
warnx("Could not read device link for %s", dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = strrchr(device, '/');
|
||||
if (result)
|
||||
result++;
|
||||
else
|
||||
result = device;
|
||||
strcpy(slot, result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void get_device_node(struct zpci_device *pdev)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
@@ -236,7 +203,7 @@ static void get_device_node(struct zpci_device *pdev)
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name);
|
||||
if (!sysfs_get_slot_addr(dev, slot))
|
||||
if (util_sys_get_dev_addr(dev, slot) != 0)
|
||||
continue;
|
||||
if (strcmp(slot, pdev->slot) == 0) {
|
||||
pdev->device = dev;
|
||||
@@ -253,8 +220,12 @@ static int device_exists(char *dev)
|
||||
char *path;
|
||||
int rc = 0;
|
||||
|
||||
/* In case a device node is specified, this will be sufficiant */
|
||||
if (util_path_exists(dev) && !util_path_is_dir(dev))
|
||||
return 1;
|
||||
|
||||
path = util_path_sysfs("bus/pci/devices/%s", dev);
|
||||
if (util_path_exists(path) || util_path_exists(dev))
|
||||
if (util_path_exists(path))
|
||||
rc = 1;
|
||||
free(path);
|
||||
|
||||
@@ -268,7 +239,7 @@ static void get_device_info(struct zpci_device *pdev, char *dev)
|
||||
if (is_blk_dev(dev))
|
||||
errx(EXIT_FAILURE, "Unsupported device type %s", dev);
|
||||
if (is_char_dev(dev)) {
|
||||
if (!sysfs_get_slot_addr(dev, pdev->slot))
|
||||
if (util_sys_get_dev_addr(dev, pdev->slot) != 0)
|
||||
errx(EXIT_FAILURE,
|
||||
"Could not determine slot address for %s", dev);
|
||||
pdev->device = dev;
|
||||
|
||||
Reference in New Issue
Block a user