diff --git a/.gitignore b/.gitignore index 91b3f12..55bf67c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ src_vipa-2.0.4.tar.gz /s390-tools-1.19.0.tar.bz2 /s390-tools-1.20.0.tar.bz2 /s390-tools-1.23.0.tar.bz2 +/src_vipa-2.1.0.tar.gz diff --git a/s390-tools-1.23.0-fedora.patch b/s390-tools-1.23.0-fedora.patch index 9e0d3e6..dd10e97 100644 --- a/s390-tools-1.23.0-fedora.patch +++ b/s390-tools-1.23.0-fedora.patch @@ -1,7 +1,7 @@ From 9b225fac81186176075f673dfe5cf8e373b2068a Mon Sep 17 00:00:00 2001 From: Dan Horak Date: Sun, 20 Jul 2008 09:24:05 +0200 -Subject: [PATCH 01/12] s390-tools-1.5.3-zipl-zfcpdump-2 +Subject: [PATCH 01/27] s390-tools-1.5.3-zipl-zfcpdump-2 --- common.mak | 4 ++-- @@ -23,13 +23,13 @@ index 44adc6e..4373da5 100644 export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_RD -- -1.8.5.3 +1.9.3 From a3d9221076f9eb7cc8434baac71327f786351c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Thu, 23 Apr 2009 11:46:01 +0200 -Subject: [PATCH 02/12] s390-tools-1.8.1-fdasd-su +Subject: [PATCH 02/27] s390-tools-1.8.1-fdasd-su --- fdasd/fdasd.c | 10 ++++++---- @@ -57,13 +57,13 @@ index ba22475..f2ac417 100644 if (anc->verbose) printf("disk layout check : ok\n"); -- -1.8.5.3 +1.9.3 From d13c754f68ea838a47b8125006b9b493cfbbb7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Wed, 21 Aug 2013 12:13:30 +0200 -Subject: [PATCH 03/12] dbginfo.sh: Avoiding exclusion list for pipes in sysfs +Subject: [PATCH 03/27] dbginfo.sh: Avoiding exclusion list for pipes in sysfs Description: dbginfo.sh: Avoiding exclusion list for pipes in sysfs Symptom: The dbginfo.sh script hangs @@ -127,13 +127,13 @@ index 6d07132..0ada40b 100755 touch "${WORKDIR_BASE}${SCRIPTNAME}".lock fi -- -1.8.5.3 +1.9.3 From 7d540e7f40c731092ac655d1d38af7d69ceee706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Wed, 21 Aug 2013 12:13:58 +0200 -Subject: [PATCH 04/12] zipl: Fix zipl "--force" option for DASD multi-volume +Subject: [PATCH 04/27] zipl: Fix zipl "--force" option for DASD multi-volume dump Description: zipl: Fix zipl "--force" option for DASD multi-volume dump @@ -178,13 +178,13 @@ index f1cec78..529d6b3 100644 .Lmem_upper_limit: .long 0xffffffff,0xffffffff # can be used for memsize=xxx -- -1.8.5.3 +1.9.3 From 21caf0d0dc05c5e950f369f72027a203a7d3e772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 5 Nov 2013 12:23:18 +0100 -Subject: [PATCH 05/12] zipl: Use "possible_cpus" kernel parameter +Subject: [PATCH 05/27] zipl: Use "possible_cpus" kernel parameter Description: zipl: Use "possible_cpus" kernel parameter Symptom: The zfcpdump system might run out-of memory. @@ -218,13 +218,13 @@ index cc2ed16..68dffe1 100644 return result; } -- -1.8.5.3 +1.9.3 From d3792e20601152ac2deea8d592b9fc176590ec5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 19 Nov 2013 18:02:03 +0100 -Subject: [PATCH 06/12] dbginfo.sh: enhancements for script execution and man +Subject: [PATCH 06/27] dbginfo.sh: enhancements for script execution and man page Description: dbginfo.sh: enhancements for script execution and man page @@ -780,13 +780,13 @@ index cdef849..c0975cc 100644 Run the script with root authority. .br -- -1.8.5.3 +1.9.3 From 31cd858e82efd289c4ea8ea4801346746aefcd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Tue, 19 Nov 2013 18:02:35 +0100 -Subject: [PATCH 07/12] dbginfo.sh: avoid double data collection +Subject: [PATCH 07/27] dbginfo.sh: avoid double data collection Description: dbginfo.sh: avoid double data collection Symptom: Execution of dbginfo.sh fails with 'no space left on device' @@ -828,13 +828,13 @@ index 9b64076..e83774b 100755 ######################################## -- -1.8.5.3 +1.9.3 From 4009f4a16c96f7fee65d77de112ef61109fdc0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Wed, 15 Jan 2014 15:08:29 +0100 -Subject: [PATCH 08/12] zipl: fix segmentation fault in automenu array +Subject: [PATCH 08/27] zipl: fix segmentation fault in automenu array Description: zipl: fix segmentation fault in automenu array Symptom: Building an automenu with a large number of entries may @@ -871,13 +871,13 @@ index 597b01c..c357418 100644 new_scan = misc_malloc(size); if (!new_scan) -- -1.8.5.3 +1.9.3 From 5eca8bced9faf6a15bdb7a0c43b53b6817a53473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Wed, 29 Jan 2014 10:37:03 +0100 -Subject: [PATCH 09/12] zipl: Fix zfcpdump "struct job_ipl_data" initialization +Subject: [PATCH 09/27] zipl: Fix zfcpdump "struct job_ipl_data" initialization Description: zipl: Fix zfcpdump "struct job_ipl_data" initialization Symptom: When zfcpdump starts, dump fails and the following error @@ -913,13 +913,13 @@ index 68dffe1..d573eda 100644 ipl.image_addr = dump_fs->image_addr; ipl.ramdisk = dump_fs->ramdisk; -- -1.8.5.3 +1.9.3 From 89e147e16348335cdfe6438e43171e7848e94dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Mon, 3 Feb 2014 09:55:38 +0100 -Subject: [PATCH 10/12] znetconf,lsqeth: Allow for 16-char network interface +Subject: [PATCH 10/27] znetconf,lsqeth: Allow for 16-char network interface name Description: znetconf,lsqeth: Allow for 16-char network interface names @@ -985,13 +985,13 @@ index 73bbe32..87c881b 100755 then printf "$LIST_FORMAT_STRING" "Device IDs" "Type" \ -- -1.8.5.3 +1.9.3 From 78560f75fa2ce043ff63647cc1618f69251dbbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Mon, 10 Feb 2014 10:20:51 +0100 -Subject: [PATCH 11/12] znetconf: Allow for 16-char network interface names +Subject: [PATCH 11/27] znetconf: Allow for 16-char network interface names Description: znetconf: Allow for 16-char network interface names Symptom: In the output of `znetconf -r/R` interface name is truncated. @@ -1028,13 +1028,13 @@ index 87c881b..09f0904 100755 return 0 } -- -1.8.5.3 +1.9.3 From 883724cff09a02a19268a47102816e161a4b01af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Mon, 10 Feb 2014 10:21:23 +0100 -Subject: [PATCH 12/12] qetharp: Allow for 16-char network interface names +Subject: [PATCH 12/27] qetharp: Allow for 16-char network interface names Description: qetharp: Allow for 16-char network interface names Symptom: In the output of `qetharp -p` interface name is truncated. @@ -1088,5 +1088,10232 @@ index 58debdc..5eccda3 100644 (flags==OSA_TR_FLAGS)? "tr":"n/a", opin->dev_name); -- -1.8.5.3 +1.9.3 + + +From e7c5ab26f13a84df52c6cd772eb80d1d44c7db49 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 14:45:08 +0200 +Subject: [PATCH 13/27] qetharp: limit interface name argument to 15 characters + +Description: qetharp: limit interface name argument to 15 characters +Symptom: when given interface name argument 16 characters long, + `qetharp` crashes with buffer overflow diagnostic. +Problem: Interface name argument is checked to be 16 characters + or less long. If it is 16 characters, strcpy() is used + to copy the value into a 16 byte buffer, which attempts + to write 17 bytes (including the trailing zero byte), + overrunning the buffer. +Solution: Because the real limit imposed by the kernel on the + interface name is 15 characters, the check for the + argument size is updated, allowing 15 characters at + most. Thus, strcpy() will never overrun the 16 byte + buffer. +Reproduction: run any variation of `qetharp` command that accepts the + interface name argument, e.g. `qetharp -q `, + giving it the name which is 16 characters long. This + results in a crash with this diagnostic: + *** buffer overflow detected ***: qetharp terminated + ======= Backtrace: ========= + /lib64/libc.so.6(__fortify_fail+0x3a)[0x3fffd2985fa] + ...etc... +--- + qetharp/qetharp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qetharp/qetharp.c b/qetharp/qetharp.c +index 5eccda3..c7126cf 100644 +--- a/qetharp/qetharp.c ++++ b/qetharp/qetharp.c +@@ -470,7 +470,7 @@ qetharp_usage(void) + static int + qetharp_parse_info(struct option_info *opin) + { +- if (opin->dev_name && (strlen(opin->dev_name) > IFNAMSIZ)) { ++ if (opin->dev_name && (strlen(opin->dev_name) >= IFNAMSIZ)) { + printf("\nError: interface name too long\n"); + return -1; + } +-- +1.9.3 + + +From 2e0a9e2e947035334264de9ada288ae3daad4779 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 14:45:46 +0200 +Subject: [PATCH 14/27] dbginfo.sh: Add collection of journalctl + +Description: dbginfo.sh: Add collection of journalctl +Symptom: * Incomplete data collection. Specifically, the journalctl is + not collected + * Findings when performing checkbashism test + * Findings when performing shellcheck test + * Messages generated by the script do not indicate the + originator of the message +Problem: * journalctl is needed to be collected + * ip route, ip ntable and lscpu are not collected + * checkbashism identified some bash built-in functions been used + along with HOSTNAME + * shellcheck identified old-style command substitution along + with some quoting issues + * Messages generated by the script can not be identified to be + generated by the script. This can be confusing in certain + situations + * journalctl, ip route, ip ntable and lscpu are not collected + * List of authors +Solution: * Adding journalctl and the other missing command to the data + collection. In terms of journalctl, the data collection is + limited to the last 5 days and 50000 lines + * Using /bin/sh functions only and replaced HOSTNAME by using + the related commands to get the hostname of the system + * Replaced the old-style command substitution with the 'modern' + one and solved the quoting issues + * Messages generated by the script print the script name along + with the severity of the message + * Removed list of authors +Reproduction: * Check the data collection for missing information such as + journalctl + * Use checkbashism on dbginfo.sh + * Use shellcheck on dbginfo.sh + * Check the output of dbginfo.sh for the missing information +--- + scripts/dbginfo.sh | 179 ++++++++++++++++++++++++++++------------------------- + 1 file changed, 93 insertions(+), 86 deletions(-) + +diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh +index e83774b..b290202 100755 +--- a/scripts/dbginfo.sh ++++ b/scripts/dbginfo.sh +@@ -1,16 +1,10 @@ + #!/bin/sh + ############################################################################### +-# Copyright IBM Corp. 2002, 2013 ++# Copyright IBM Corp. 2002, 2014 + # + # Collect some configuration, trace, and debug information about the + # Linux on System z machine + # +-# Author(s): Sven Schuetz +-# Wolfgang Taphorn +-# Stefan Reimbold +-# Susanne Wintenberger +-# Michael Mueller +-# + # This file is part of the s390-tools + # + # s390-tools is free software; you can redistribute it and/or modify +@@ -29,10 +23,11 @@ + ############################################################################### + + # Switching to neutral locale +-export LC_ALL=C ++LC_ALL=C ++export LC_ALL + + # The kernel release version as delivered from uname -r +-readonly KERNEL_RELEASE_VERSION="`uname -r 2>/dev/null`" ++readonly KERNEL_RELEASE_VERSION=$(uname -r 2>/dev/null) + + ######################################## + # Global used variables +@@ -42,25 +37,28 @@ readonly KERNEL_RELEASE_VERSION="`uname -r 2>/dev/null`" + readonly SCRIPTNAME="${0##*/}" + + # The terminal +-readonly TERMINAL="`tty 2>/dev/null`" ++readonly TERMINAL=$(tty 2>/dev/null) ++ ++# The hostname of the system ++readonly SYSTEMHOSTNAME=$(hostname -s 2>/dev/null) + + # The processor ID for the first processor +-readonly PROCESSORID=`grep -E ".*processor 0:.*" /proc/cpuinfo | sed 's/.*identification[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g'` ++readonly PROCESSORID=$(grep -E ".*processor 0:.*" /proc/cpuinfo | sed 's/.*identification[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g') + + # The processor version for the first processor +-readonly PROCESSORVERSION=`grep -E ".*processor 0:.*" /proc/cpuinfo | sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g'` ++readonly PROCESSORVERSION=$(grep -E ".*processor 0:.*" /proc/cpuinfo | sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g') + + # The current date +-readonly DATETIME=`date +%Y-%m-%d-%H-%M-%S 2>/dev/null` ++readonly DATETIME=$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null) + + # The base working directory + readonly WORKDIR_BASE="/tmp/" + + # The current working directory for the actual script execution +-if test -z ${PROCESSORID}; then +- readonly WORKDIR_CURRENT="DBGINFO-"${DATETIME}"-`hostname -s 2>/dev/null`" ++if test -z "${PROCESSORID}"; then ++ readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}" + else +- readonly WORKDIR_CURRENT="DBGINFO-"${DATETIME}"-`hostname -s 2>/dev/null`-${PROCESSORID}" ++ readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}-${PROCESSORID}" + fi + + # The current path where the collected information is put together +@@ -84,6 +82,9 @@ readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfsfiles.out" + # File that includes content of OSA OAT + readonly OUTPUT_FILE_OSAOAT="${WORKPATH}osa_oat" + ++# File that includes the output of journalctl ++readonly OUTPUT_FILE_JOURNALCTL="${WORKPATH}journalctl.out" ++ + # Mount point of the debug file system + readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug" + +@@ -91,34 +92,34 @@ readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug" + readonly COLLECTION_COUNT=7 + + # The kernel version (e.g. '2' from 2.6.32 or '3' from 3.2.1) +-readonly KERNEL_VERSION="`uname -r 2>/dev/null | cut -d'.' -f1`" ++readonly KERNEL_VERSION=$(uname -r 2>/dev/null | cut -d'.' -f1) + + # The kernel major revision number (e.g. '6' from 2.6.32 or '2' from 3.2.1) +-readonly KERNEL_MAJOR_REVISION="`uname -r 2>/dev/null | cut -d'.' -f2`" ++readonly KERNEL_MAJOR_REVISION=$(uname -r 2>/dev/null | cut -d'.' -f2) + + # The kernel mainor revision number (e.g. '32' from 2.6.32 or '1' from 3.2.1) +-readonly KERNEL_MINOR_REVISION="`uname -r 2>/dev/null | cut -d'.' -f3 | sed s/[^0-9].*//g`" ++readonly KERNEL_MINOR_REVISION=$(uname -r 2>/dev/null | cut -d'.' -f3 | sed 's/[^0-9].*//g') + + # Is this kernel supporting sysfs - since 2.4 (0=yes, 1=no) +-if test ${KERNEL_VERSION} -lt 2 || +- ( test ${KERNEL_VERSION} -eq 2 && test ${KERNEL_MAJOR_REVISION} -le 4 ); then ++if test "${KERNEL_VERSION}" -lt 2 || ++ ( test "${KERNEL_VERSION}" -eq 2 && test "${KERNEL_MAJOR_REVISION}" -le 4 ); then + readonly LINUX_SUPPORT_SYSFS=1 + else + readonly LINUX_SUPPORT_SYSFS=0 + fi + + # Is this kernel potentially using the /sys/kernel/debug feature - since 2.6.13 (0=yes, 1=no) +-if test ${KERNEL_VERSION} -lt 2 || +- ( test ${KERNEL_VERSION} -eq 2 && +- ( test ${KERNEL_MAJOR_REVISION} -lt 6 || +- ( test ${KERNEL_MAJOR_REVISION} -eq 6 && test ${KERNEL_MINOR_REVISION} -lt 13 ))); then ++if test "${KERNEL_VERSION}" -lt 2 || ++ ( test "${KERNEL_VERSION}" -eq 2 && ++ ( test "${KERNEL_MAJOR_REVISION}" -lt 6 || ++ ( test "${KERNEL_MAJOR_REVISION}" -eq 6 && test "${KERNEL_MINOR_REVISION}" -lt 13 ))); then + readonly LINUX_SUPPORT_SYSFSDBF=1 + else + readonly LINUX_SUPPORT_SYSFSDBF=0 + fi + + if test "x${PROCESSORVERSION}" = "xFF" || test "x${PROCESSORVERSION}" = "xff"; then +- readonly RUNTIME_ENVIRONMENT=`grep -E "VM00.*Control Program.*" /proc/sysinfo| sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g'`; ++ readonly RUNTIME_ENVIRONMENT=$(grep -E "VM00.*Control Program.*" /proc/sysinfo| sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g') + else + readonly RUNTIME_ENVIRONMENT="LPAR" + fi +@@ -165,12 +166,12 @@ PROCFILES="\ + # Adding files to PROCFILES in case scsi devices are available + if test -e /proc/scsi; then + PROCFILES="${PROCFILES}\ +- `find /proc/scsi -type f -perm /444 2>/dev/null`\ ++ $(find /proc/scsi -type f -perm /444 2>/dev/null)\ + " + fi + + # Adding files to PROCFILES in case we run on Kernel 2.4 or older +-if test ${LINUX_SUPPORT_SYSFS} -eq 1; then ++if test "${LINUX_SUPPORT_SYSFS}" -eq 1; then + PROCFILES="${PROCFILES}\ + /proc/chpids\ + /proc/chandev\ +@@ -181,10 +182,10 @@ if test ${LINUX_SUPPORT_SYSFS} -eq 1; then + fi + + # Adding s390dbf files to PROCFILE in case we run on Kernel lower than 2.6.13 +-if test ${LINUX_SUPPORT_SYSFSDBF} -eq 1; then ++if test "${LINUX_SUPPORT_SYSFSDBF}" -eq 1; then + if test -e /proc/s390dbf; then + PROCFILES="${PROCFILES}\ +- `find /proc/s390dbf -type f -not -path \"*/raw\" -not -path \"*/flush\" 2>/dev/null`\ ++ $(find /proc/s390dbf -type f -not -path "*/raw" -not -path "*/flush" 2>/dev/null)\ + " + fi + fi +@@ -246,7 +247,7 @@ CONFIGFILES="\ + /etc/udev*\ + /etc/xinet.d\ + /etc/*release\ +- `find /lib/modules -name modules.dep 2>/dev/null`\ ++ $(find /lib/modules -name modules.dep 2>/dev/null)\ + " + + ######################################## +@@ -265,9 +266,11 @@ CMDS="uname -a\ + :nm-tool\ + :route -n\ + :ip route list\ ++ :ip route list table all\ + :ip rule list\ + :ip neigh list\ + :ip link show\ ++ :ip ntable\ + :ipcs -a\ + :netstat -pantu\ + :netstat -s\ +@@ -282,6 +285,7 @@ CMDS="uname -a\ + :lsqeth\ + :lschp\ + :lscss\ ++ :lscpu -ae\ + :lsmem\ + :lsdasd\ + :ziorep_config -ADM\ +@@ -311,6 +315,7 @@ CMDS="uname -a\ + :java -version\ + :cat /root/.bash_history\ + :env\ ++ :journalctl --all --no-pager --since=$(date -d '5 day ago' +%Y-%m-%d) --until=now --lines=50000 > ${OUTPUT_FILE_JOURNALCTL}\ + " + + ######################################## +@@ -404,26 +409,26 @@ collect_vmcmdsout() { + if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then + pr_syslog_stdout "2 of ${COLLECTION_COUNT}: Collecting z/VM command output" + +- if type vmcp >/dev/null 2>&1; then ++ if which vmcp >/dev/null 2>&1; then + cp_command="vmcp" + if ! lsmod 2>/dev/null | grep -q vmcp; then + modprobe vmcp && module_loaded=0 && sleep 2 + fi +- elif type hcp >/dev/null 2>&1; then ++ elif which hcp >/dev/null 2>&1; then + cp_command="hcp" + if ! lsmod 2>/dev/null | grep -q cpint; then + modprobe cpint && module_loaded=0 && sleep 2 + fi + else + pr_log_stdout " " +- pr_log_stdout "WARNING: No program found to communicate to z/VM CP" +- pr_log_stdout "WARNING: Skipping the collection of z/VM command output" ++ pr_log_stdout "${SCRIPTNAME}: Warning: No program found to communicate to z/VM CP" ++ pr_log_stdout " Skipping collection of z/VM command output" + pr_log_stdout " " + return 1 + fi +- VMUSERID="`${cp_command} q userid 2>/dev/null | sed -ne 's/^\([^[:space:]]*\).*$/\1/p'`" ++ VMUSERID=$(${cp_command} q userid 2>/dev/null | sed -ne 's/^\([^[:space:]]*\).*$/\1/p') + +- vm_cmds="`echo ${VM_CMDS} | sed "s/VMUSERID/${VMUSERID}/g"`" ++ vm_cmds=$(echo "${VM_CMDS}" | sed "s/VMUSERID/${VMUSERID}/g") + + IFS=: + for vm_command in ${vm_cmds}; do +@@ -431,9 +436,9 @@ collect_vmcmdsout() { + local cp_buffer_size=2 + local rc_buffer_size=2 + while test ${rc_buffer_size} -eq 2 && test ${cp_buffer_size} -lt 1024; do +- cp_buffer_size=$(( ${cp_buffer_size} * 2 )) ++ cp_buffer_size=$(( cp_buffer_size * 2 )) + +- eval ${cp_command} -b ${cp_buffer_size}k ${vm_command} >/dev/null 2>&1 ++ eval ${cp_command} -b ${cp_buffer_size}k "${vm_command}" >/dev/null 2>&1 + rc_buffer_size=$? + done + call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" "${OUTPUT_FILE_VMCMD}" +@@ -470,22 +475,22 @@ collect_procfs() { + + ######################################## + collect_sysfs() { +- local debugfs_mounted=0; ++ local debugfs_mounted=0 + local file_name + local file_names + local rc_mount + + # Requires kernel version newer then 2.4 +- if test ${LINUX_SUPPORT_SYSFS} -eq 0; then ++ if test "${LINUX_SUPPORT_SYSFS}" -eq 0; then + pr_syslog_stdout "4 of ${COLLECTION_COUNT}: Collecting sysfs" + # Requires kernel version of 2.6.13 or newer +- if test ${LINUX_SUPPORT_SYSFSDBF} -eq 0; then ++ if test "${LINUX_SUPPORT_SYSFSDBF}" -eq 0; then + if ! grep -qE "${MOUNT_POINT_DEBUGFS}.*debugfs" /proc/mounts 2>/dev/null; then + if mount -t debugfs debugfs "${MOUNT_POINT_DEBUGFS}" >/dev/null 2>&1; then + sleep 2 +- debugfs_mounted=1; ++ debugfs_mounted=1 + else +- pr_log_stdout "WARNING: \"Unable to mount debugfs ${MOUNT_POINT_DEBUGFS}\"" ++ pr_log_stdout "${SCRIPTNAME}: Warning: Unable to mount debugfs at \"${MOUNT_POINT_DEBUGFS}\"" + fi + fi + fi +@@ -498,14 +503,16 @@ collect_sysfs() { + + find /sys -noleaf -type f -perm /444 2>/dev/null | while IFS= read -r file_name; do + echo " ${file_name}" +- dd if="${file_name}" iflag=nonblock of="${WORKPATH}${file_name}" ++ if ! dd if="${file_name}" status=noxfer iflag=nonblock of="${WORKPATH}${file_name}" >/dev/null 2>&1; then ++ echo "${SCRIPTNAME}: Warning: failed to copy \"${file_name}\"" ++ fi + done + + if test ${debugfs_mounted} -eq 1; then + umount "${MOUNT_POINT_DEBUGFS}" + fi + else +- pr_syslog_stdout "4 of ${COLLECTION_COUNT}: Collecting sysfs skipped. Kernel `uname -r` must be newer than 2.4" ++ pr_syslog_stdout "4 of ${COLLECTION_COUNT}: Collecting sysfs skipped. Kernel $(uname -r) must be newer than 2.4" + fi + + pr_log_stdout " " +@@ -542,13 +549,13 @@ collect_configfiles() { + + ######################################## + collect_osaoat() { +- local network_devices="`lsqeth 2>/dev/null | grep "Device name" | sed 's/.*:[[:space:]]\+\([^[:space:]]*\)[[:space:]]\+/\1/g'`" ++ local network_devices=$(lsqeth 2>/dev/null | grep "Device name" | sed 's/.*:[[:space:]]\+\([^[:space:]]*\)[[:space:]]\+/\1/g') + local network_device + + if which qethqoat >/dev/null 2>&1; then + if test -n "${network_devices}"; then + pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output" +- for network_device in "${network_devices}"; do ++ for network_device in ${network_devices}; do + call_run_command "qethqoat ${network_device}" "${OUTPUT_FILE_OSAOAT}.out" && + call_run_command "qethqoat -r ${network_device}" "${OUTPUT_FILE_OSAOAT}_${network_device}.raw" + done +@@ -569,23 +576,23 @@ collect_osaoat() { + call_run_command() { + local cmd="${1}" + local logfile="${2}" +- local raw_cmd="`echo ${cmd} | sed -ne 's/^\([^[:space:]]*\).*$/\1/p'`" ++ local raw_cmd=$(echo "${cmd}" | sed -ne 's/^\([^[:space:]]*\).*$/\1/p') + + echo "#######################################################" >> "${logfile}" +- echo "${USER}@${HOSTNAME}> ${cmd}" >> "${logfile}" ++ echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}" + + # check if command exists + if ! which "${raw_cmd}" >/dev/null 2>&1; then + # check if command is a builtin + if ! command -v "${raw_cmd}" >/dev/null 2>&1; then +- echo "WARNING: Command \"${raw_cmd}\" not available" >> "${logfile}" ++ echo "${SCRIPTNAME}: Warning: Command \"${raw_cmd}\" not available" >> "${logfile}" + echo >> "${logfile}" +- return 1; ++ return 1 + fi + fi + + if ! eval "${cmd}" >> "${logfile}" 2>&1; then +- echo "WARNING: Command \"${cmd}\" failed" >> "${logfile}" ++ echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}" + echo >> "${logfile}" + return 1 + else +@@ -602,7 +609,7 @@ call_collect_file() { + + echo " ${file_name}" + +- directory_name="`dirname \"${file_name}\" 2>/dev/null`" ++ directory_name=$(dirname "${file_name}" 2>/dev/null) + if test ! -e "${WORKPATH}${directory_name}"; then + mkdir -p "${WORKPATH}${directory_name}" 2>&1 + fi +@@ -622,7 +629,7 @@ call_collect_file() { + print_version() { + cat </dev/null; then +- echo "ERROR: Target directory ${WORKPATH} already exists or" +- echo " ${WORKDIR_BASE} does not exist!" ++ echo "${SCRIPTNAME}: Error: Target directory \"${WORKPATH}\" already exists or" ++ echo " \"${WORKDIR_BASE}\" does not exist!" + exit 1 + fi + } +@@ -746,9 +753,9 @@ create_package() + + if ! tar -czf "${WORKARCHIVE}" "${WORKDIR_CURRENT}"; then + pr_stdout " " +- pr_stdout "ERROR: Collection of data failed!" +- pr_stdout " The creation of ${WORKARCHIVE} was not successful." +- pr_stdout " Please check the directory ${WORKDIR_BASE}" ++ pr_stdout "${SCRIPTNAME}: Error: Collection of data failed!" ++ pr_stdout " The creation of \"${WORKARCHIVE}\" was not successful." ++ pr_stdout " Please check the directory \"${WORKDIR_BASE}\"" + pr_stdout " to provide enough free available space." + else + pr_stdout " " +@@ -766,14 +773,14 @@ environment_cleanup() + { + if ! rm -rf "${WORKPATH}" 2>/dev/null; then + pr_stdout " " +- pr_stdout "WARNING: Deletion of ${WORKPATH} failed" +- pr_stdout "Please remove the directory manually" ++ pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKPATH}\" failed" ++ pr_stdout " Please remove the directory manually" + pr_stdout " " + fi + if ! rm -f "${WORKDIR_BASE}${SCRIPTNAME}".lock 2>/dev/null; then + pr_stdout " " +- pr_stdout "WARNING: Deletion of ${WORKDIR_BASE}${SCRIPTNAME} failed" +- pr_stdout "Please remove the file manually" ++ pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKDIR_BASE}${SCRIPTNAME}\" failed" ++ pr_stdout " Please remove the file manually" + pr_stdout " " + fi + } +@@ -784,14 +791,14 @@ environment_cleanup() + emergency_exit() + { + pr_stdout " " +- pr_stdout "INFO: Data collection has been interrupted" +- pr_stdout "INFO: Cleanup of temporary collected data" ++ pr_stdout "${SCRIPTNAME}: Info: Data collection has been interrupted" ++ pr_stdout " Cleanup of temporary collected data" + environment_cleanup +- pr_stdout "INFO: Emergency exit processed" ++ pr_stdout "${SCRIPTNAME}: Info: Emergency exit processed" + + pr_stdout " " + logger -t "${SCRIPTNAME}" "Data collection interrupted" +- exit; ++ exit + } + + +@@ -818,18 +825,18 @@ pr_syslog_stdout() + { + echo "$@" + echo "$@" >&8 +- logger -t ${SCRIPTNAME} "$@" ++ logger -t "${SCRIPTNAME}" "$@" + } + + + ############################################################################### + # Running the script + +-commandline_parse ${*} ++commandline_parse "${@}" + + # Verification to run as root +-if test `/usr/bin/id -u 2>/dev/null` -ne 0; then +- echo "ERROR: You must be user root to run ${SCRIPTNAME}!" ++if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then ++ echo "${SCRIPTNAME}: Error: You must be user root to run \"${SCRIPTNAME}\"!" + exit 1 + fi + +@@ -837,14 +844,14 @@ environment_setup + print_version + + # saving stdout/stderr and redirecting stdout/stderr into log file +-exec 8>&1 9>&2 >${LOGFILE} 2>&1 ++exec 8>&1 9>&2 >"${LOGFILE}" 2>&1 + +-# trap on SIGHUP SIGINT SIGTERM +-trap emergency_exit 1 2 15 ++# trap on SIGHUP=1 SIGINT=2 SIGTERM=15 ++trap emergency_exit SIGHUP SIGINT SIGTERM + + pr_log_stdout "" +-pr_log_stdout "Hardware platform = `uname -i`" +-pr_log_stdout "Kernel version = ${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION} (`uname -r 2>/dev/null`)" ++pr_log_stdout "Hardware platform = $(uname -i)" ++pr_log_stdout "Kernel version = ${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION} ($(uname -r 2>/dev/null))" + pr_log_stdout "Runtime environment = ${RUNTIME_ENVIRONMENT}" + pr_log_stdout "" + +-- +1.9.3 + + +From faee7005cdb796752c7e98855f237c2dd815b8e3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 14:47:06 +0200 +Subject: [PATCH 15/27] zdsfs: backport of prerequisite util_list cleanup + +Summary: zdsfs: backport of prerequisite util_list cleanup +Description: The zdsfs implementation uses the s390-tools common code list + implementation util_list. This is the backport of some + util_list interface cleanup changes that are a prerequisite + for the zdsfs feature patch. +--- + cmsfs-fuse/cmsfs-fuse.c | 8 ++-- + cmsfs-fuse/config.c | 2 +- + hyptop/sd.h | 4 +- + hyptop/sd_core.c | 20 ++++----- + hyptop/table.c | 50 +++++++++++------------ + hyptop/table.h | 2 +- + hyptop/tbox.c | 8 ++-- + include/util.h | 40 ++++++++++-------- + libutil/util_list.c | 105 +++++++++++++++++++++++++++++++++++++++--------- + zdump/dfi.c | 10 ++--- + zdump/dfi.h | 4 +- + zdump/dfo.c | 4 +- + 12 files changed, 166 insertions(+), 91 deletions(-) + +diff --git a/cmsfs-fuse/cmsfs-fuse.c b/cmsfs-fuse/cmsfs-fuse.c +index 33d9709..fb86fd4 100644 +--- a/cmsfs-fuse/cmsfs-fuse.c ++++ b/cmsfs-fuse/cmsfs-fuse.c +@@ -600,7 +600,7 @@ static struct file *file_open(const char *name) + strncpy(uc_name, name, MAX_FNAME); + str_toupper(uc_name); + +- util_list_entry_iterate(&open_file_list, f) ++ util_list_iterate(&open_file_list, f) + if (strncmp(f->path + 1, uc_name, MAX_FNAME) == 0) + return f; + return NULL; +@@ -1342,7 +1342,7 @@ static int is_textfile(struct fst_entry *fst) + memset(type, 0, sizeof(type)); + ebcdic_dec(type, fst->type, 8); + +- util_list_entry_iterate(&text_type_list, ft) ++ util_list_iterate(&text_type_list, ft) + if (strncmp(ft->name, type, strlen(ft->name)) == 0) + return 1; + return 0; +@@ -1852,7 +1852,7 @@ static int cmsfs_open(const char *path, struct fuse_file_info *fi) + str_toupper(f->path); + + f->use_count = 1; +- util_list_entry_add_head(&open_file_list, f); ++ util_list_add_head(&open_file_list, f); + } else + f->use_count++; + +@@ -4318,7 +4318,7 @@ static int cmsfs_release(const char *path, struct fuse_file_info *fi) + if (f->use_count == 1) { + if (f->unlinked) + delete_file(f->path); +- util_list_entry_remove(&open_file_list, f); ++ util_list_remove(&open_file_list, f); + destroy_file_object(f); + } else + f->use_count--; +diff --git a/cmsfs-fuse/config.c b/cmsfs-fuse/config.c +index 9e4ebde..5492664 100644 +--- a/cmsfs-fuse/config.c ++++ b/cmsfs-fuse/config.c +@@ -62,7 +62,7 @@ static void add_filetype(char *name, struct util_list *list) + if (entry == NULL) + DIE_PERROR("malloc failed"); + strncpy(entry->name, name, MAX_TYPE_LEN); +- util_list_entry_add_head(list, entry); ++ util_list_add_head(list, entry); + } + + static int filetype_valid(const char *type, int line) +diff --git a/hyptop/sd.h b/hyptop/sd.h +index f84541e..db5c435 100644 +--- a/hyptop/sd.h ++++ b/hyptop/sd.h +@@ -418,10 +418,10 @@ void sd_dg_register(struct sd_dg *); + * Iterators + */ + #define sd_sys_iterate(parent, sys) \ +- util_list_entry_iterate(&parent->child_list, sys) ++ util_list_iterate(&parent->child_list, sys) + + #define sd_cpu_iterate(parent, cpu) \ +- util_list_entry_iterate(&parent->cpu_list, cpu) ++ util_list_iterate(&parent->cpu_list, cpu) + + #define sd_sys_item_iterate(ptr, i) \ + for (i = 0; (ptr = sd.dg->sys_item_vec[i]); i++) +diff --git a/hyptop/sd_core.c b/hyptop/sd_core.c +index 61b9a03..dd016b3 100644 +--- a/hyptop/sd_core.c ++++ b/hyptop/sd_core.c +@@ -176,7 +176,7 @@ struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char* id) + { + struct sd_cpu *cpu; + +- util_list_entry_iterate(&sys->cpu_list, cpu) { ++ util_list_iterate(&sys->cpu_list, cpu) { + if (strcmp(cpu->id, id) == 0) + return cpu; + } +@@ -213,7 +213,7 @@ struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id, + cpu->d_cur = &cpu->d1; + cpu->cnt = cnt; + +- util_list_entry_add_tail(&parent->cpu_list, cpu); ++ util_list_add_tail(&parent->cpu_list, cpu); + + return cpu; + } +@@ -225,7 +225,7 @@ struct sd_sys *sd_sys_get(struct sd_sys *parent, const char* id) + { + struct sd_sys *sys; + +- util_list_entry_iterate(&parent->child_list, sys) { ++ util_list_iterate(&parent->child_list, sys) { + if (strcmp(sys->id, id) == 0) + return sys; + } +@@ -247,7 +247,7 @@ struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id) + if (parent) { + sys_new->i.parent = parent; + parent->child_cnt++; +- util_list_entry_add_tail(&parent->child_list, sys_new); ++ util_list_add_tail(&parent->child_list, sys_new); + } + return sys_new; + } +@@ -298,9 +298,9 @@ void sd_sys_update_start(struct sd_sys *sys) + sys->child_cnt_active = 0; + sys->cpu_cnt_active = 0; + +- util_list_entry_iterate(&sys->cpu_list, cpu) ++ util_list_iterate(&sys->cpu_list, cpu) + l_cpu_update_start(cpu); +- util_list_entry_iterate(&sys->child_list, child) ++ util_list_iterate(&sys->child_list, child) + sd_sys_update_start(child); + } + +@@ -315,10 +315,10 @@ static void l_cpu_update_end(struct sd_sys *sys) + if (sys->cpu_cnt_active == sys->cpu_cnt) + return; + +- util_list_entry_iterate_safe(&sys->cpu_list, cpu, tmp) { ++ util_list_iterate_safe(&sys->cpu_list, cpu, tmp) { + if (!cpu->i.active) { + /* CPU has not been updated, remove it */ +- util_list_entry_remove(&sys->cpu_list, cpu); ++ util_list_remove(&sys->cpu_list, cpu); + sd_cpu_free(cpu); + continue; + } +@@ -337,10 +337,10 @@ static void l_sys_update_end(struct sd_sys *sys) + + l_cpu_update_end(sys); + +- util_list_entry_iterate_safe(&sys->child_list, child, tmp) { ++ util_list_iterate_safe(&sys->child_list, child, tmp) { + if (!child->i.active) { + /* child has not been updated, remove it */ +- util_list_entry_remove(&sys->child_list, child); ++ util_list_remove(&sys->child_list, child); + sd_sys_free(child); + continue; + } +diff --git a/hyptop/table.c b/hyptop/table.c +index e2d62d7..adecaf6 100644 +--- a/hyptop/table.c ++++ b/hyptop/table.c +@@ -29,7 +29,7 @@ static int l_row_is_marked(struct table *t, struct table_row *row) + { + struct table_mark_key *key; + +- util_list_entry_iterate(&t->mark_key_list, key) { ++ util_list_iterate(&t->mark_key_list, key) { + if (strcmp(row->entries[0].str, key->str) == 0) + return 1; + } +@@ -45,7 +45,7 @@ static void l_mark_key_add(struct table *t, char *str) + + key = ht_zalloc(sizeof(*key)); + strncpy(key->str, str, sizeof(key->str)); +- util_list_entry_add_tail(&t->mark_key_list, key); ++ util_list_add_tail(&t->mark_key_list, key); + t->mark_keys_cnt++; + } + +@@ -56,9 +56,9 @@ static void l_mark_key_remove(struct table *t, char *str) + { + struct table_mark_key *key, *tmp; + +- util_list_entry_iterate_safe(&t->mark_key_list, key, tmp) { ++ util_list_iterate_safe(&t->mark_key_list, key, tmp) { + if (strcmp(str, key->str) == 0) { +- util_list_entry_remove(&t->mark_key_list, key); ++ util_list_remove(&t->mark_key_list, key); + ht_free(key); + t->mark_keys_cnt--; + return; +@@ -74,10 +74,10 @@ void table_row_mark_del_all(struct table *t) + struct table_mark_key *key, *tmp; + struct table_row *row; + +- util_list_entry_iterate(&t->row_list, row) ++ util_list_iterate(&t->row_list, row) + row->marked = 0; +- util_list_entry_iterate_safe(&t->mark_key_list, key, tmp) { +- util_list_entry_remove(&t->mark_key_list, key); ++ util_list_iterate_safe(&t->mark_key_list, key, tmp) { ++ util_list_remove(&t->mark_key_list, key); + ht_free(key); + } + t->mark_keys_cnt = 0; +@@ -108,7 +108,7 @@ void table_row_mark_toggle_by_key(struct table *t, const char *str) + { + struct table_row *row; + +- util_list_entry_iterate(&t->row_list, row) { ++ util_list_iterate(&t->row_list, row) { + if (strcmp(str, row->entries[0].str) == 0) + table_row_mark_toggle(t, row); + } +@@ -253,8 +253,8 @@ void table_row_del_all(struct table *t) + { + struct table_row *row, *tmp; + +- util_list_entry_iterate_safe(&t->row_list, row, tmp) { +- util_list_entry_remove(&t->row_list, row); ++ util_list_iterate_safe(&t->row_list, row, tmp) { ++ util_list_remove(&t->row_list, row); + table_row_free(row); + } + l_row_last_init(t); +@@ -399,7 +399,7 @@ static void l_row_last_calc(struct table *t) + struct table_row *row; + + l_row_last_init(t); +- util_list_entry_iterate(&t->row_list, row) { ++ util_list_iterate(&t->row_list, row) { + if (t->mode_hide_unmarked && !row->marked) + continue; + l_row_last_agg(t, row); +@@ -426,16 +426,16 @@ void table_row_add(struct table *t, struct table_row *row) + l_row_format(t, row); + + if (util_list_is_empty(&t->row_list) || !t->attr_sorted_table) { +- util_list_entry_add_tail(&t->row_list, row); ++ util_list_add_tail(&t->row_list, row); + } else { +- util_list_entry_iterate(&t->row_list, tmp) { ++ util_list_iterate(&t->row_list, tmp) { + if (l_row_less_than(t, tmp, row)) + break; + } + if (tmp) +- util_list_entry_add_prev(&t->row_list, row, tmp); ++ util_list_add_prev(&t->row_list, row, tmp); + else +- util_list_entry_add_tail(&t->row_list, row); ++ util_list_add_tail(&t->row_list, row); + } + if (l_row_is_marked(t, row)) { + row->marked = 1; +@@ -455,7 +455,7 @@ void table_rebuild(struct table *t) + + table_col_iterate(t, col, i) + l_col_max_width_init(t, col); +- util_list_entry_iterate(&t->row_list, row) ++ util_list_iterate(&t->row_list, row) + l_row_format(t, row); + l_row_format(t, t->row_last); + } +@@ -478,21 +478,21 @@ static void l_table_sort(struct table *t) + while (!util_list_is_empty(&t->row_list)) { + row_min = NULL; + +- util_list_entry_iterate(&t->row_list, row) { ++ util_list_iterate(&t->row_list, row) { + if (row_min == NULL) + row_min = row; + else if (l_row_less_than(t, row, row_min)) + row_min = row; + } +- util_list_entry_remove(&t->row_list, row_min); +- util_list_entry_add_head(&list, row_min); ++ util_list_remove(&t->row_list, row_min); ++ util_list_add_head(&list, row_min); + } + /* + * Copy temp list to original list + */ +- util_list_entry_iterate_safe(&list, row, tmp) { +- util_list_entry_remove(&list, row); +- util_list_entry_add_tail(&t->row_list, row); ++ util_list_iterate_safe(&list, row, tmp) { ++ util_list_remove(&list, row); ++ util_list_add_tail(&t->row_list, row); + } + } + +@@ -587,7 +587,7 @@ static struct table_row *l_selected_row(struct table *t) + struct table_row *row; + int row_nr = 0; + +- util_list_entry_iterate(&t->row_list, row) { ++ util_list_iterate(&t->row_list, row) { + if (t->mode_hide_unmarked && !row->marked) + continue; + if (row_nr == t->row_nr_select) +@@ -906,7 +906,7 @@ static void l_table_print_curses(struct table *t) + l_status_update(t); + l_headline_print(t); + l_unitline_print(t); +- util_list_entry_iterate(&t->row_list, row) { ++ util_list_iterate(&t->row_list, row) { + if (t->mode_hide_unmarked && !row->marked) + continue; + if (row_nr < t->row_nr_begin) { +@@ -960,7 +960,7 @@ static void l_table_print_all(struct table *t) + + l_headline_print(t); + l_unitline_print(t); +- util_list_entry_iterate(&t->row_list, row) { ++ util_list_iterate(&t->row_list, row) { + l_row_print(t, row); + hyptop_print_nl(); + } +diff --git a/hyptop/table.h b/hyptop/table.h +index ac4fa72..132eed9 100644 +--- a/hyptop/table.h ++++ b/hyptop/table.h +@@ -419,6 +419,6 @@ static inline void table_row_entry_str_add(struct table_row *table_row, + * Interate over all mark keys + */ + #define table_iterate_mark_keys(t, key) \ +- util_list_entry_iterate(&t->mark_key_list, key) ++ util_list_iterate(&t->mark_key_list, key) + + #endif /* TABLE_H */ +diff --git a/hyptop/tbox.c b/hyptop/tbox.c +index 80f6888..af2223a 100644 +--- a/hyptop/tbox.c ++++ b/hyptop/tbox.c +@@ -29,8 +29,8 @@ void tbox_line_del_all(struct tbox *tb) + { + struct tbox_line *line, *tmp; + +- util_list_entry_iterate_safe(&tb->line_list, line, tmp) { +- util_list_entry_remove(&tb->line_list, line); ++ util_list_iterate_safe(&tb->line_list, line, tmp) { ++ util_list_remove(&tb->line_list, line); + l_line_free(line); + } + tb->tbox_ready = 0; +@@ -56,7 +56,7 @@ void tbox_line_add(struct tbox *tb, const char *str) + assert(0); + line = ht_zalloc(sizeof(*line)); + line->str = ht_strdup(str); +- util_list_entry_add_tail(&tb->line_list, line); ++ util_list_add_tail(&tb->line_list, line); + tb->last_line = line; + tb->line_cnt++; + } +@@ -202,7 +202,7 @@ void tbox_print(struct tbox *tb) + return; + + l_adjust_values(tb); +- util_list_entry_iterate(&tb->line_list, line) { ++ util_list_iterate(&tb->line_list, line) { + if (line_nr < tb->line_start) { + line_nr++; + continue; +diff --git a/include/util.h b/include/util.h +index c528bbe..c6bfb8e 100644 +--- a/include/util.h ++++ b/include/util.h +@@ -32,26 +32,34 @@ struct util_list_node { + void util_list_free(struct util_list *list); + struct util_list *util_list_new_offset(unsigned long offset); + void util_list_init_offset(struct util_list *list, unsigned long offset); +-void util_list_entry_add_tail(struct util_list *list, void *entry); +-void util_list_entry_add_head(struct util_list *list, void *entry); +-void util_list_entry_add_next(struct util_list *list, void *entry, +- void *list_entry); +-void util_list_entry_add_prev(struct util_list *list, void *entry, +- void *list_entry); +-void util_list_entry_remove(struct util_list *list, void *entry); +-void *util_list_entry_next(struct util_list *list, void *entry); +-void *util_list_entry_prev(struct util_list *list, void *entry); +-void *util_list_entry_start(struct util_list *list); ++void util_list_add_tail(struct util_list *list, void *entry); ++void util_list_add_head(struct util_list *list, void *entry); ++void util_list_add_next(struct util_list *list, void *entry, void *list_entry); ++void util_list_add_prev(struct util_list *list, void *entry, void *list_entry); ++void util_list_remove(struct util_list *list, void *entry); ++void *util_list_next(struct util_list *list, void *entry); ++void *util_list_prev(struct util_list *list, void *entry); ++void *util_list_start(struct util_list *list); + int util_list_is_empty(struct util_list *list); ++unsigned long util_list_cnt(struct util_list *list); + +-#define util_list_entry_iterate(list, i) \ +- for (i = util_list_entry_start(list); \ ++/* ++ * The compare function should return the following: ++ * a < b --> < 0 ++ * a > b --> > 0 ++ * a = b --> = 0 ++ */ ++typedef int (*util_list_cmp_fn)(void *a, void *b, void *data); ++void util_list_sort(struct util_list *list, util_list_cmp_fn fn, void *data); ++ ++#define util_list_iterate(list, i) \ ++ for (i = util_list_start(list); \ + i != NULL; \ +- i = util_list_entry_next(list, i)) \ ++ i = util_list_next(list, i)) \ + +-#define util_list_entry_iterate_safe(list, i, n) \ +- for (i = util_list_entry_start(list), n = util_list_entry_next(list, i); \ ++#define util_list_iterate_safe(list, i, n) \ ++ for (i = util_list_start(list), n = util_list_next(list, i); \ + i != NULL; \ +- i = n, n = util_list_entry_next(list, i)) \ ++ i = n, n = util_list_next(list, i)) \ + + #endif /* UTIL_H */ +diff --git a/libutil/util_list.c b/libutil/util_list.c +index 07feee8..429f246 100644 +--- a/libutil/util_list.c ++++ b/libutil/util_list.c +@@ -12,6 +12,22 @@ + #include "util.h" + + /* ++ * Node to entry ++ */ ++static inline void *n2e(struct util_list *list, struct util_list_node *node) ++{ ++ return ((void *) node) - list->offset; ++} ++ ++/* ++ * Entry to node ++ */ ++static inline struct util_list_node *e2n(struct util_list *list, void *entry) ++{ ++ return entry + list->offset; ++} ++ ++/* + * Initialize linked list + */ + void util_list_init_offset(struct util_list *list, unsigned long offset) +@@ -44,9 +60,9 @@ void util_list_free(struct util_list *list) + /* + * Add new element to end of list + */ +-void util_list_entry_add_tail(struct util_list *list, void *entry) ++void util_list_add_tail(struct util_list *list, void *entry) + { +- struct util_list_node *node = entry + list->offset; ++ struct util_list_node *node = e2n(list, entry); + + node->next = NULL; + if (!list->start) { +@@ -62,9 +78,9 @@ void util_list_entry_add_tail(struct util_list *list, void *entry) + /* + * Add new element to front of list + */ +-void util_list_entry_add_head(struct util_list *list, void *entry) ++void util_list_add_head(struct util_list *list, void *entry) + { +- struct util_list_node *node = entry + list->offset; ++ struct util_list_node *node = e2n(list, entry); + + node->prev = NULL; + node->next = NULL; +@@ -80,11 +96,11 @@ void util_list_entry_add_head(struct util_list *list, void *entry) + /* + * Add new element (entry) after an existing element (list_entry) + */ +-void util_list_entry_add_next(struct util_list *list, void *entry, ++void util_list_add_next(struct util_list *list, void *entry, + void *list_entry) + { +- struct util_list_node *node = entry + list->offset; +- struct util_list_node *list_node = list_entry + list->offset; ++ struct util_list_node *node = e2n(list, entry); ++ struct util_list_node *list_node = e2n(list, list_entry); + + node->next = list_node->next; + node->prev = list_node; +@@ -98,11 +114,11 @@ void util_list_entry_add_next(struct util_list *list, void *entry, + /* + * Add new element (entry) before an existing element (list_entry) + */ +-void util_list_entry_add_prev(struct util_list *list, void *entry, ++void util_list_add_prev(struct util_list *list, void *entry, + void *list_entry) + { +- struct util_list_node *node = entry + list->offset; +- struct util_list_node *list_node = list_entry + list->offset; ++ struct util_list_node *node = e2n(list, entry); ++ struct util_list_node *list_node = e2n(list, list_entry); + + node->prev = list_node->prev; + node->next = list_node; +@@ -116,9 +132,9 @@ void util_list_entry_add_prev(struct util_list *list, void *entry, + /* + * Remove element from list + */ +-void util_list_entry_remove(struct util_list *list, void *entry) ++void util_list_remove(struct util_list *list, void *entry) + { +- struct util_list_node *node = entry + list->offset; ++ struct util_list_node *node = e2n(list, entry); + + if (list->start == node) + list->start = node->next; +@@ -133,7 +149,7 @@ void util_list_entry_remove(struct util_list *list, void *entry) + /* + * Get first element of list + */ +-void *util_list_entry_start(struct util_list *list) ++void *util_list_start(struct util_list *list) + { + if (!list->start) + return NULL; +@@ -143,33 +159,84 @@ void *util_list_entry_start(struct util_list *list) + /* + * Get next element after entry + */ +-void *util_list_entry_next(struct util_list *list, void *entry) ++void *util_list_next(struct util_list *list, void *entry) + { + struct util_list_node *node; + + if (!entry) + return NULL; +- node = entry + list->offset; ++ node = e2n(list, entry); + node = node->next; + if (!node) + return NULL; +- return ((void *) node) - list->offset; ++ return n2e(list, node); + } + + /* + * Get previous element before entry + */ +-void *util_list_entry_prev(struct util_list *list, void *entry) ++void *util_list_prev(struct util_list *list, void *entry) + { + struct util_list_node *node; + + if (!entry) + return NULL; +- node = entry + list->offset; ++ node = e2n(list, entry); + node = node->prev; + if (!node) + return NULL; +- return ((void *) node) - list->offset; ++ return n2e(list, node); ++} ++ ++/* ++ * Get number of list entries ++ */ ++unsigned long util_list_cnt(struct util_list *list) ++{ ++ unsigned long cnt = 0; ++ void *entry; ++ ++ util_list_iterate(list, entry) ++ cnt++; ++ return cnt; ++} ++ ++/* ++ * Sort table (bubble sort) ++ */ ++void util_list_sort(struct util_list *list, util_list_cmp_fn cmp_fn, ++ void *data) ++{ ++ struct util_list_node *node1, *node2; ++ unsigned long list_cnt, i, j; ++ void *entry1, *entry2; ++ ++ list_cnt = util_list_cnt(list); ++ ++ for (i = 1; i < list_cnt; i++) { ++ node1 = list->start; ++ for (j = 0; j < list_cnt - i; j++) { ++ node2 = node1->next; ++ entry1 = n2e(list, node1); ++ entry2 = n2e(list, node2); ++ if (cmp_fn(entry1, entry2, data) > 0) { ++ node1->next = node2->next; ++ if (node1->next) ++ node1->next->prev = node1; ++ else ++ list->end = node1; ++ node2->next = node1; ++ node2->prev = node1->prev; ++ if (node2->prev) ++ node2->prev->next = node2; ++ else ++ list->start = node2; ++ node1->prev = node2; ++ } else { ++ node1 = node2; ++ } ++ } ++ } + } + + /* +diff --git a/zdump/dfi.c b/zdump/dfi.c +index 2590a8d..7defd5d 100644 +--- a/zdump/dfi.c ++++ b/zdump/dfi.c +@@ -201,7 +201,7 @@ void dfi_mem_chunk_add(u64 start, u64 size, void *data, + mem_chunk->read_fn = read_fn; + mem_chunk->data = data; + +- util_list_entry_add_tail(&l.mem.chunk_list, mem_chunk); ++ util_list_add_tail(&l.mem.chunk_list, mem_chunk); + l.mem.start_addr = MIN(l.mem.start_addr, mem_chunk->start); + l.mem.end_addr = MAX(l.mem.end_addr, mem_chunk->end); + l.mem.chunk_cache = mem_chunk; +@@ -241,7 +241,7 @@ struct dfi_mem_chunk *dfi_mem_chunk_first(void) + { + if (util_list_is_empty(&l.mem.chunk_list)) + return NULL; +- return util_list_entry_start(&l.mem.chunk_list); ++ return util_list_start(&l.mem.chunk_list); + } + + /* +@@ -249,7 +249,7 @@ struct dfi_mem_chunk *dfi_mem_chunk_first(void) + */ + struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk) + { +- return util_list_entry_next(&l.mem.chunk_list, mem_chunk); ++ return util_list_next(&l.mem.chunk_list, mem_chunk); + } + + /* +@@ -257,7 +257,7 @@ struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk) + */ + struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *mem_chunk) + { +- return util_list_entry_prev(&l.mem.chunk_list, mem_chunk); ++ return util_list_prev(&l.mem.chunk_list, mem_chunk); + } + + /* +@@ -309,7 +309,7 @@ struct dfi_cpu *dfi_cpu_alloc(void) + */ + void dfi_cpu_add(struct dfi_cpu *cpu) + { +- util_list_entry_add_tail(&l.cpus.list, cpu); ++ util_list_add_tail(&l.cpus.list, cpu); + l.cpus.cnt++; + } + +diff --git a/zdump/dfi.h b/zdump/dfi.h +index 03852c6..bcf52d7 100644 +--- a/zdump/dfi.h ++++ b/zdump/dfi.h +@@ -111,7 +111,7 @@ enum dfi_cpu_content { + }; + + #define dfi_cpu_iterate(cpu) \ +- util_list_entry_iterate(dfi_cpu_list(), cpu) ++ util_list_iterate(dfi_cpu_list(), cpu) + + extern struct util_list *dfi_cpu_list(void); + extern void dfi_cpu_info_init(enum dfi_cpu_content content); +@@ -153,7 +153,7 @@ extern struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr); + + extern struct util_list *dfi_mem_chunk_list(void); + #define dfi_mem_chunk_iterate(mem_chunk) \ +- util_list_entry_iterate(dfi_mem_chunk_list(), mem_chunk) ++ util_list_iterate(dfi_mem_chunk_list(), mem_chunk) + + /* + * Dump header attribute set/get functions +diff --git a/zdump/dfo.c b/zdump/dfo.c +index e068a02..d0060be 100644 +--- a/zdump/dfo.c ++++ b/zdump/dfo.c +@@ -11,7 +11,7 @@ + #include "zgetdump.h" + + #define dfo_chunk_iterate(dfo_chunk) \ +- util_list_entry_iterate(&l.dump.chunk_list, dfo_chunk) ++ util_list_iterate(&l.dump.chunk_list, dfo_chunk) + + /* + * DFO vector +@@ -53,7 +53,7 @@ void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn) + dfo_chunk->size = size; + dfo_chunk->data = data; + dfo_chunk->read_fn = read_fn; +- util_list_entry_add_head(&l.dump.chunk_list, dfo_chunk); ++ util_list_add_head(&l.dump.chunk_list, dfo_chunk); + l.dump.chunk_cnt++; + l.dump.size = MAX(l.dump.size, dfo_chunk->end + 1); + } +-- +1.9.3 + + +From 4153f8fe9846e32e92e2d1a23b64c81835d4c60c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 14:59:04 +0200 +Subject: [PATCH 16/27] zdsfs: add new tool to access z/OS data sets + +Summary: zdsfs: add new tool to access z/OS data sets +Description: This feature introduces the new tool zdsfs, which is a FUSE file + system that allows access to z/OS data sets. This tool works on + DASD devices with enabled raw track access mode. + The tool dasdview now also supports devices in raw track access + mode. +--- + Makefile | 4 +- + dasdview/Makefile | 6 +- + dasdview/dasdview.8 | 39 +- + dasdview/dasdview.c | 1296 ++++++++++++++---- + dasdview/dasdview.h | 12 +- + include/libzds.h | 812 +++++++++++ + include/vtoc.h | 15 +- + libzds/Makefile | 22 + + libzds/libzds.c | 3774 +++++++++++++++++++++++++++++++++++++++++++++++++++ + zdsfs/Makefile | 22 + + zdsfs/zdsfs.1 | 270 ++++ + zdsfs/zdsfs.c | 1032 ++++++++++++++ + 12 files changed, 7029 insertions(+), 275 deletions(-) + create mode 100644 include/libzds.h + create mode 100644 libzds/Makefile + create mode 100644 libzds/libzds.c + create mode 100644 zdsfs/Makefile + create mode 100644 zdsfs/zdsfs.1 + create mode 100644 zdsfs/zdsfs.c + +diff --git a/Makefile b/Makefile +index 23904da..22728b9 100644 +--- a/Makefile ++++ b/Makefile +@@ -3,11 +3,11 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar + # Include commond definitions + include common.mak + +-LIB_DIRS = libvtoc libu2s libutil ++LIB_DIRS = libvtoc libu2s libutil libzds + SUB_DIRS = $(LIB_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 \ +- ziomon iucvterm hyptop cmsfs-fuse qethqoat ++ ziomon iucvterm hyptop cmsfs-fuse qethqoat zdsfs + + all: subdirs_make + +diff --git a/dasdview/Makefile b/dasdview/Makefile +index 518a65c..df1378f 100644 +--- a/dasdview/Makefile ++++ b/dasdview/Makefile +@@ -1,12 +1,14 @@ + include ../common.mak + + CPPFLAGS += -I../include -DSYSFS ++LDLIBS += -lzds ++LDFLAGS += -L../libzds + + all: dasdview + +-dasdview.o: dasdview.h ../include/zt_common.h ++dasdview.o: dasdview.h ../include/zt_common.h ../include/libzds.h + +-dasdview: dasdview.o ../libvtoc/vtoc.o ../libu2s/u2s.o ++dasdview: dasdview.o ../libu2s/u2s.o + + install: all + $(INSTALL) -d -m 755 $(BINDIR) $(MANDIR)/man8 +diff --git a/dasdview/dasdview.8 b/dasdview/dasdview.8 +index 3a5cdef..d193a11 100644 +--- a/dasdview/dasdview.8 ++++ b/dasdview/dasdview.8 +@@ -9,7 +9,7 @@ to the console. + .br + [-i] [-x] [-j] [-c] + .br +- [-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}] ++ [-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf3\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}] + .br + {-n \fIdevno\fR|-f \fInode\fR} \fIdevice\fR + .SH DESCRIPTION +@@ -20,6 +20,21 @@ The \fIdevice\fR is the node of the device (e.g. '/dev/dasda'). + Any device node created by udev for kernel 2.6 can be used + (e.g. '/dev/dasd/0.0.b100/disc'). + ++DASD devices in raw_track_access mode are supported and detected ++automatically. When in raw_track_access mode, the same basic ++functions are available as in the regular mode, but the output may ++have a slightly different layout: ++.IP \(bu 2 ++The disk dump functions (\fB-b\fR and \fB-s\fR) print the count, ++key and data information for the whole track, and not just the ++contents of the data areas. ++.IP \(bu 2 ++The VTOC listing (\fB-t\fR) print all specified DSCBs in the same ++format as in the regular mode, but in the sequence as they appear in ++the VTOC. The \fB-t info\fR overview contains more details for each ++data set than in the regular mode, to support the larger variety of ++data set layouts. ++ + .SH OPTIONS + .TP + \fB-h\fR or \fB--help\fR +@@ -43,13 +58,17 @@ If no size is specified dasdview will take the default size. The variable + The default for \fIbegin\fR is \fI0\fR. + .br + +-\fBNote:\fR dasdview will show you the content of your disk using the DASD ++\fBNote 1:\fR dasdview will show you the content of your disk using the DASD + driver. If this driver decides to hide or add some parts of the disk, you have + to live with it. This happens for example with the first two tracks of a + cdl-formatted disk. In this case the DASD driver fills up shorter blocks with + zeros to have a constant blocksize. And all applications, including dasdview, + believe it. + .br ++\fBNote 2:\fR In raw_track_access mode \fIbegin\fR must be aligned to ++track boundaries. A simple way to do that is to specify a track or ++cylinder as starting point. ++.br + + examples: + .br +@@ -77,7 +96,13 @@ value is specified dasdview will take the default start value. The variable + size[k|m|b|t|c] + + .br +-The default for \fIsize\fR is \fI128\fR. ++\fBNote:\fR In raw_track_access mode \fIsize\fR must be a multiple of ++one track. A simple way to do that is to specify the size in tracks or ++cylinders. ++ ++.br ++The default for \fIsize\fR is \fI128\fR in regular mode and \fI1t\fR ++in raw_track_access mode. + .br + + examples: +@@ -112,7 +137,8 @@ you will get 8 Bytes per line in hex, ascii and ebcdic. And in addition a line + number and a decimal and hexadecimal byte count will be printed. + .br + The \fB-2\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR +-options. ++options. In raw_track_access mode this format is not supported and the ++option will be ignored. + + .TP + \fB-i\fR or \fB--info\fR +@@ -153,6 +179,11 @@ systems would see (e.g. data set names and sizes). + Print the content of all format 1 DSCBs. + .br + ++\fIf3\fR: ++.br ++Print the content of all format 3 DSCBs. ++.br ++ + \fIf4\fR: + .br + Print the content of the format 4 DSCB. +diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c +index 2909b16..5e26530 100644 +--- a/dasdview/dasdview.c ++++ b/dasdview/dasdview.c +@@ -2,7 +2,7 @@ + * File...........: s390-tools/dasdview/dasdview.c + * Author(s)......: Volker Sameske + * Gerhard Tonn +- * Copyright IBM Corp. 2001, 2006. ++ * Copyright IBM Corp. 2001, 2013. + */ + + #define _LARGEFILE64_SOURCE /* needed for unistd.h */ +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -28,6 +29,8 @@ + #include "vtoc.h" + #include "dasdview.h" + #include "u2s.h" ++#include "libzds.h" ++ + + /* Characters per line */ + #define DASDVIEW_CPL 16 +@@ -42,6 +45,8 @@ static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2006"; + #define ERROR_STRING_SIZE 1024 + static char error_string[ERROR_STRING_SIZE]; + ++#define min(x, y) ((x) > (y) ? (y) : (x)) ++ + /* + * Generate and print an error message based on the formatted + * text string FMT and a variable amount of extra arguments. +@@ -74,7 +79,7 @@ dasdview_usage(void) + printf("\nprints DASD information:\n\n"); + printf("dasdview [-b begin] [-s size] [-1|-2] \n" + " [-i] [-x] [-j] [-c]\n" +- " [-l] [-t {info|f1|f4|f5|f7|f8|f9|all}] \n" ++ " [-l] [-t {info|f1|f3|f4|f5|f7|f8|f9|all}] \n" + " [-h] [-v] \n" + " {-n devno|-f node} device\n" + "\nwhere:\n" +@@ -165,11 +170,38 @@ dot (char label[]) { + } + } + ++ ++/* ++ * Attempts to find the sysfs entry for the given busid and reads ++ * the contents of a specified attribute to the buffer ++ */ ++static int dasdview_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; ++} ++ + static void + dasdview_get_info(dasdview_info_t *info) + { + int fd; + struct dasd_eckd_characteristics *characteristics; ++ char buffer[10]; ++ int rc; + + fd = open(info->device, O_RDONLY); + if (fd == -1) +@@ -221,9 +253,23 @@ dasdview_get_info(dasdview_info_t *info) + info->hw_cylinders = characteristics->long_no_cyl; + else + info->hw_cylinders = characteristics->no_cyl; ++ close(fd); + + +- close(fd); ++ if(u2s_getbusid(info->device, info->busid) == -1) ++ info->busid_valid = 0; ++ else ++ info->busid_valid = 1; ++ ++ rc = dasdview_read_attribute(info->busid, "raw_track_access", buffer, ++ sizeof(buffer)); ++ if (rc) { ++ zt_error_print("dasdview: Could not retrieve raw_track_access" ++ " mode information."); ++ return; ++ } ++ if ('1' == buffer[0]) ++ info->raw_track_access = 1; + } + + +@@ -244,27 +290,53 @@ dasdview_parse_input(unsigned long long *p, dasdview_info_t *info, char *s) + suffix = tolower(*endp); + } else + suffix = 0; +- switch (suffix) { +- case 'k': +- l *= 1024LL; +- break; +- case 'm': +- l *= 1024LL * 1024LL; +- break; +- case 't': +- l *= (unsigned long long) info->blksize * +- (unsigned long long) info->geo.sectors; +- break; +- case 'b': +- l *= (unsigned long long) info->blksize; +- break; +- case 'c': +- l *= (unsigned long long) info->blksize * +- (unsigned long long) info->geo.sectors * +- (unsigned long long) info->geo.heads; +- break; +- default: +- break; ++ if (info->raw_track_access) { ++ switch (suffix) { ++ case 't': ++ l *= RAWTRACKSIZE; ++ break; ++ case 'c': ++ l *= (unsigned long long) info->geo.heads * ++ RAWTRACKSIZE; ++ break; ++ case 0: ++ if (l % RAWTRACKSIZE) { ++ zt_error_print("dasdview: only full tracks can" ++ " be accessd on devices with " ++ " raw_track_access enabled.\n", s); ++ ++ goto error; ++ } ++ break; ++ default: ++ zt_error_print("dasdview: only types t and c are" ++ " allowed for devices with" ++ " raw_track_access enabled.\n", s); ++ goto error; ++ } ++ } else { ++ switch (suffix) { ++ case 'k': ++ l *= 1024LL; ++ break; ++ case 'm': ++ l *= 1024LL * 1024LL; ++ break; ++ case 't': ++ l *= (unsigned long long) info->blksize * ++ (unsigned long long) info->geo.sectors; ++ break; ++ case 'b': ++ l *= (unsigned long long) info->blksize; ++ break; ++ case 'c': ++ l *= (unsigned long long) info->blksize * ++ (unsigned long long) info->geo.sectors * ++ (unsigned long long) info->geo.heads; ++ break; ++ default: ++ break; ++ } + } + *p = l; + +@@ -289,22 +361,19 @@ dasdview_print_general_info(dasdview_info_t *info) + unsigned char a,b,c; + char suffix[sizeof(buf.release)]; + int rc; +- char busid[U2S_BUS_ID_SIZE]; + + rc = uname(&buf); + if(!rc) + { + sscanf(buf.release, "%c.%c.%c-%s", &a, &b, &c, suffix); +- if(KERNEL_VERSION(2,5,0) <= KERNEL_VERSION(a, b, c)) +- { +- if(u2s_getbusid(info->device, busid) == -1) ++ if(KERNEL_VERSION(2,5,0) <= KERNEL_VERSION(a, b, c)) { ++ if (info->busid_valid) ++ printf("busid : %s\n", ++ info->busid); ++ else + printf("busid :" + " \n"); +- else +- printf("busid : %s\n", busid); +- } +- else +- { ++ } else { + #endif + printf("device number : hex %x \tdec %d\n", + info->dasd_info.devno, +@@ -461,12 +530,23 @@ static void + dasdview_print_vlabel(dasdview_info_t *info) + { + volume_label_t vlabel; +- ++ volume_label_t *tmpvlabel; ++ int rc; + unsigned char s4[5], t4[5], s5[6], t5[6], s6[7], t6[7]; + char s14[15], t14[15], s29[30], t29[30]; + int i; + +- dasdview_read_vlabel(info, &vlabel); ++ if (info->raw_track_access) { ++ rc = lzds_dasd_read_vlabel(info->dasd); ++ if (rc) { ++ zt_error_print("error when reading label from device:" ++ " rc=%d\n", rc); ++ exit(-1); ++ } ++ lzds_dasd_get_vlabel(info->dasd, &tmpvlabel); ++ memcpy(&vlabel, tmpvlabel, sizeof(vlabel)); ++ } else ++ dasdview_read_vlabel(info, &vlabel); + + printf("\n--- volume label -----------------------------" \ + "---------------------------------\n"); +@@ -586,10 +666,22 @@ static void + dasdview_print_volser(dasdview_info_t *info) + { + volume_label_t vlabel; ++ volume_label_t *tmpvlabel; + char volser[7]; + char vollbl[5]; ++ int rc; + +- dasdview_read_vlabel(info, &vlabel); ++ if (info->raw_track_access) { ++ rc = lzds_dasd_read_vlabel(info->dasd); ++ if (rc) { ++ zt_error_print("error when reading label from device:" ++ " rc=%d\n", rc); ++ exit(-1); ++ } ++ lzds_dasd_get_vlabel(info->dasd, &tmpvlabel); ++ memcpy(&vlabel, tmpvlabel, sizeof(vlabel)); ++ } else ++ dasdview_read_vlabel(info, &vlabel); + + bzero(vollbl, 5); + bzero(volser, 7); +@@ -806,14 +898,268 @@ static void dasdview_print_vtoc_info(dasdview_info_t *info) + } + } + ++static void dasdview_print_short_info_extent_raw(extent_t *ext) ++{ ++ if (ext->typeind > 0x00) ++ printf(" %3d (%5d,%5d) (%5d,%5d)\n", ++ ext->seqno, ++ vtoc_get_cyl_from_cchh(&ext->llimit), ++ vtoc_get_head_from_cchh(&ext->llimit), ++ vtoc_get_cyl_from_cchh(&ext->ulimit), ++ vtoc_get_head_from_cchh(&ext->ulimit)); ++} ++ ++static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1, ++ dasdview_info_t *info) ++{ ++ char s6[7], s13[14], s44[45]; ++ unsigned long long j; ++ format3_label_t *f3; ++ format9_label_t *f9; ++ struct dscb *dscb; ++ int rc; ++ ++ bzero(s44, 45); ++ strncpy(s44, f1->DS1DSNAM, 44); ++ vtoc_ebcdic_dec(s44, s44, 44); ++ bzero(s6, 7); ++ strncpy(s6, (char *)f1->DS1DSSN, 6); ++ vtoc_ebcdic_dec(s6, s6, 6); ++ bzero(s13, 14); ++ strncpy(s13, (char *)f1->DS1SYSCD, 13); ++ vtoc_ebcdic_dec(s13, s13, 13); ++ ++ printf("data set name : '%44s'\n", s44); ++ printf("data set serial number : '%6s'\n", s6); ++ printf("system code : '%13s'\n", s13); ++ printf("creation date : year %4d, day %3d\n", ++ f1->DS1CREDT.year + 1900, f1->DS1CREDT.day); ++ ++ printf("flags : "); ++ if (f1->DS1FLAG1 & 0x80) ++ printf("DS1COMPR "); ++ if (f1->DS1FLAG1 & 0x40) ++ printf("DS1CPOIT "); ++ if (f1->DS1FLAG1 & 0x20) ++ printf("DS1EXPBY "); ++ if (f1->DS1FLAG1 & 0x10) ++ printf("DS1RECAL "); ++ if (f1->DS1FLAG1 & 0x08) ++ printf("DS1LARGE "); ++ if (f1->DS1FLAG1 & 0x04) ++ printf("unknown "); ++ if ((f1->DS1FLAG1 & 0x01) && (f1->DS1FLAG1 & 0x02)) ++ printf("DS1EATTR=not used "); ++ if (!(f1->DS1FLAG1 & 0x01) && (f1->DS1FLAG1 & 0x02)) ++ printf("DS1EATTR=optional "); ++ if ((f1->DS1FLAG1 & 0x01) && !(f1->DS1FLAG1 & 0x02)) ++ printf("DS1EATTR=no "); ++ if (f1->DS1FLAG1 & 0x00) ++ printf("DS1EATTR=default "); ++ printf("\n"); ++ ++ printf("SMS flags : "); ++ if (f1->DS1SMSFG & 0x80) ++ printf("DS1SMSDS "); ++ if (f1->DS1SMSFG & 0x40) ++ printf("DS1SMSUC "); ++ if (f1->DS1SMSFG & 0x20) ++ printf("DS1REBLK "); ++ if (f1->DS1SMSFG & 0x10) ++ printf("DS1CRSDB "); ++ if (f1->DS1SMSFG & 0x08) ++ printf("DS1PDSE "); ++ if (f1->DS1SMSFG & 0x04) ++ printf("DS1STRP "); ++ if (f1->DS1SMSFG & 0x02) ++ printf("DS1PDSEX "); ++ if (f1->DS1SMSFG & 0x01) ++ printf("DS1DSAE "); ++ printf("\n"); ++ ++ printf("organisation : "); ++ if (f1->DS1DSRG1 & 0x80) ++ printf("DS1DSGIS "); ++ if (f1->DS1DSRG1 & 0x40) ++ printf("DS1DSGPS "); ++ if (f1->DS1DSRG1 & 0x20) ++ printf("DS1DSGDA "); ++ if (f1->DS1DSRG1 & 0x10) ++ printf("DS1DSGCX "); ++ if (f1->DS1DSRG1 & 0x08) ++ printf("reserved "); ++ if (f1->DS1DSRG1 & 0x04) ++ printf("reserved "); ++ if (f1->DS1DSRG1 & 0x02) ++ printf("DS1DSGPO "); ++ if (f1->DS1DSRG1 & 0x01) ++ printf("DS1DSGU "); ++ if (f1->DS1DSRG2 & 0x80) ++ printf("DS1DSGGS "); ++ if (f1->DS1DSRG2 & 0x40) ++ printf("DS1DSGTX "); ++ if (f1->DS1DSRG2 & 0x20) ++ printf("DS1DSGTQ "); ++ if (f1->DS1DSRG2 & 0x10) ++ printf("reserved "); ++ if (f1->DS1DSRG2 & 0x08) ++ printf("DS1ACBM "); ++ if (f1->DS1DSRG2 & 0x04) ++ printf("DS1DSGTR "); ++ if (f1->DS1DSRG2 & 0x02) ++ printf("reserved "); ++ if (f1->DS1DSRG2 & 0x01) ++ printf("reserved"); ++ printf("\n"); ++ ++ printf("record format : "); ++ if ((f1->DS1RECFM & 0x80) && !(f1->DS1RECFM & 0x40)) ++ printf("DS1RECFF "); ++ if (!(f1->DS1RECFM & 0x80) && (f1->DS1RECFM & 0x40)) ++ printf("DS1RECFV "); ++ if ((f1->DS1RECFM & 0x80) && (f1->DS1RECFM & 0x40)) ++ printf("DS1RECFU "); ++ if (f1->DS1RECFM & 0x20) ++ printf("DS1RECFT "); ++ if (f1->DS1RECFM & 0x10) ++ printf("DS1RECFB "); ++ if (f1->DS1RECFM & 0x08) ++ printf("DS1RECFS "); ++ if (f1->DS1RECFM & 0x04) ++ printf("DS1RECFA "); ++ if (f1->DS1RECFM & 0x02) ++ printf("DS1RECMC "); ++ if (f1->DS1RECFM & 0x01) ++ printf("reserved"); ++ printf("\n"); ++ ++ printf("(max) block length : %u\n", f1->DS1BLKL); ++ printf("logical record length : %u\n", f1->DS1LRECL); ++ ++ printf("extents belonging to this dataset:\n"); ++ printf(" seqno llimit (cyl, trk) ulimit (cyl, trk)\n"); ++ ++ /* The format 1 label can point to a chain of f3 labels ++ * The format 8 label points to a (chain of) f9 labels ++ * The format 9 label may contain several format 3 labels, but as ++ * far as I know, it is still OK to follow the 'next dscb' chain. ++ * So for a format 9 label I have to follow this chain until I ++ * find the first format 3 dscb and then I can follow the format 3 ++ * dscbs to the end of the chain. ++ */ ++ rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, &f1->DS1PTRDS, ++ &dscb); ++ /* The first f9 label contains extra data that we may want ++ * to print here */ ++ while (!rc && dscb && dscb->fmtid == 0xf9) { ++ f9 = (format9_label_t *)dscb; ++ rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, ++ &f9->DS9PTRDS, &dscb); ++ } ++ if (rc) { ++ zt_error_print("dasdview: Broken format 3 DSCB" ++ " chain \n"); ++ exit(-1); ++ } ++ f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL; ++ ++ /* first print the extents that are part of the f1/f8 label itself */ ++ dasdview_print_short_info_extent_raw(&f1->DS1EXT1); ++ dasdview_print_short_info_extent_raw(&f1->DS1EXT2); ++ dasdview_print_short_info_extent_raw(&f1->DS1EXT3); ++ ++ /* now follow the f3 chain into the rabbit hole */ ++ while (f3) { ++ /* sanity check */ ++ if (f3->DS3FMTID != 0xf3) { ++ zt_error_print("dasdview: Broken format 3 DSCB" ++ " chain \n"); ++ exit(-1); ++ } ++ for (j = 0; j < 4; ++j) ++ dasdview_print_short_info_extent_raw(&f3->DS3EXTNT[j]); ++ for (j = 0; j < 9; ++j) ++ dasdview_print_short_info_extent_raw(&f3->DS3ADEXT[j]); ++ rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, ++ &f3->DS3PTRDS, ++ (struct dscb **)&f3); ++ if (rc) { ++ zt_error_print("dasdview: Broken format 3 DSCB" ++ " chain \n"); ++ exit(-1); ++ } ++ } ++ printf("\n"); ++} ++ ++static void dasdview_print_vtoc_info_raw(dasdview_info_t *info) ++{ ++ struct dscbiterator *it; ++ struct dscb *dscb; ++ int rc; ++ int f1c, f3c, f4c, f5c, f7c, f8c, f9c; ++ ++ f1c = 0; ++ f3c = 0; ++ f4c = 0; ++ f5c = 0; ++ f7c = 0; ++ f8c = 0; ++ f9c = 0; ++ rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); ++ if (rc) { ++ zt_error_print("dasdview: could not allocate DSCB iterator \n"); ++ exit(-1); ++ } ++ while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { ++ if (dscb->fmtid == 0xf1) ++ ++f1c; ++ else if (dscb->fmtid == 0xf3) ++ ++f3c; ++ else if (dscb->fmtid == 0xf4) ++ ++f4c; ++ else if (dscb->fmtid == 0xf5) ++ ++f5c; ++ else if (dscb->fmtid == 0xf7) ++ ++f7c; ++ else if (dscb->fmtid == 0xf8) ++ ++f8c; ++ else if (dscb->fmtid == 0xf9) ++ ++f9c; ++ } ++ lzds_dscbiterator_free(it); ++ printf("--- VTOC info --------------------------------" \ ++ "---------------------------------\n"); ++ printf("The VTOC contains:\n"); ++ printf(" %d format 1 label(s)\n", f1c); ++ printf(" %d format 3 label(s)\n", f3c); ++ printf(" %d format 4 label(s)\n", f4c); ++ printf(" %d format 5 label(s)\n", f5c); ++ printf(" %d format 7 label(s)\n", f7c); ++ printf(" %d format 8 label(s)\n", f8c); ++ printf(" %d format 9 label(s)\n", f9c); ++ ++ rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); ++ if (rc) { ++ zt_error_print("dasdview: could not allocate DSCB iterator \n"); ++ exit(-1); ++ } ++ while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { ++ if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8) ++ dasdview_print_format1_8_short_info_raw( ++ (format1_label_t *)dscb, info); ++ } ++ lzds_dscbiterator_free(it); ++} ++ ++ + /* + * Note: the explicit cylinder/head conversion for large volume + * adresses should not be necessary for entries that point to + * vtoc labels, as those must be located in the first 65K-1 tracks, + * but we do it anyway to be on the safe side. + */ +- +-static void dasdview_print_format1_8_full(format1_label_t *f1) ++static void dasdview_print_format1_8_no_head(format1_label_t *f1) + { + char s6[7], s13[14], s44[45]; + int i; +@@ -980,298 +1326,538 @@ static void dasdview_print_format1_8_full(format1_label_t *f1) + f1->DS1PTRDS.b); + } + +-static void dasdview_print_vtoc_f1(dasdview_info_t *info) ++static void dasdview_print_vtoc_f1_raw(format1_label_t *f1) + { +- int j; +- +- printf("--- VTOC format 1 labels ----------------------" \ ++ printf("\n--- VTOC format 1 label -----------------------" + "---------------------------------\n"); ++ dasdview_print_format1_8_no_head(f1); ++} + +- if (info->f1c < 1) +- { +- printf("This VTOC doesn't contain a format 1 label.\n"); +- return; +- } ++/* Note: A format 8 label uses the same type as format 1 */ ++static void dasdview_print_vtoc_f8_raw(format1_label_t *f8) ++{ ++ printf("\n--- VTOC format 8 label -----------------------" ++ "---------------------------------\n"); ++ dasdview_print_format1_8_no_head(f8); ++} + +- for (j=0; jf1c; j++) { +- printf("\n--- format 1 DSCB number %d ---\n", j+1); +- dasdview_print_format1_8_full(&info->f1[j]); +- } ++static void dasdview_print_extent(extent_t *ext, char *name, int index) ++{ ++ printf("%s[%d] : hex %02x%02x%04x%04x%04x%04x\n", ++ name, ++ index, ++ ext->typeind, ++ ext->seqno, ++ ext->llimit.cc, ++ ext->llimit.hh, ++ ext->ulimit.cc, ++ ext->ulimit.hh); ++ printf(" typeind : dec %d, hex %02x\n", ++ ext->typeind, ++ ext->typeind); ++ printf(" seqno : dec %d, hex %02x\n", ++ ext->seqno, ext->seqno); ++ printf(" llimit : hex %04x%04x " \ ++ "(cyl %d, trk %d)\n", ++ ext->llimit.cc, ++ ext->llimit.hh, ++ vtoc_get_cyl_from_cchh(&ext->llimit), ++ vtoc_get_head_from_cchh(&ext->llimit)); ++ printf(" ulimit : hex %04x%04x " \ ++ "(cyl %d, trk %d)\n", ++ ext->ulimit.cc, ++ ext->ulimit.hh, ++ vtoc_get_cyl_from_cchh(&ext->ulimit), ++ vtoc_get_head_from_cchh(&ext->ulimit)); + } + +-static void dasdview_print_vtoc_f8(dasdview_info_t *info) ++static void dasdview_print_vtoc_f3_raw(format3_label_t *f3) + { +- int j; ++ int i; + +- printf("--- VTOC format 8 labels ----------------------" \ ++ printf("\n--- VTOC format 3 label ----------------------" + "---------------------------------\n"); + +- if (info->f8c < 1) +- { +- printf("This VTOC doesn't contain a format 8 label.\n"); +- return; +- } ++ printf("DS3KEYID : "); ++ for (i=0; i<4; i++) ++ printf("%02x", f3->DS3KEYID[i]); ++ printf("\n"); ++ ++ for (i = 0; i < 4; ++i) ++ dasdview_print_extent(&f3->DS3EXTNT[i], "DS3EXTNT", i); ++ ++ printf("DS3FMTID : dec %d, hex %02x\n", ++ f3->DS3FMTID, f3->DS3FMTID); ++ ++ for (i = 0; i < 9; ++i) ++ dasdview_print_extent(&f3->DS3ADEXT[i], "DS3ADEXT", i); ++ ++ printf("DS3PTRDS : %04x%04x%02x " \ ++ "(cyl %d, trk %d, blk %d)\n", ++ f3->DS3PTRDS.cc, f3->DS3PTRDS.hh, ++ f3->DS3PTRDS.b, ++ vtoc_get_cyl_from_cchhb(&f3->DS3PTRDS), ++ vtoc_get_head_from_cchhb(&f3->DS3PTRDS), ++ f3->DS3PTRDS.b); + +- for (j=0; jf8c; j++) { +- printf("\n--- format 8 DSCB number %d ---\n", j+1); +- dasdview_print_format1_8_full(&info->f8[j]); +- } + } + +-static void +-dasdview_print_vtoc_f4(dasdview_info_t *info) ++static void dasdview_print_vtoc_f4_raw(format4_label_t *f4) + { + int i; + +- printf("\n--- VTOC format 4 label ----------------------" \ ++ printf("\n--- VTOC format 4 label ----------------------" + "---------------------------------\n"); + +- if (info->f4c < 1) +- { +- printf("This VTOC doesn't contain a format 4 label.\n"); +- return; +- } +- + printf("DS4KEYCD : "); +- for (i=0; i<44; i++) printf("%02x", info->f4.DS4KEYCD[i]); ++ for (i=0; i<44; i++) printf("%02x", f4->DS4KEYCD[i]); + printf("\nDS4IDFMT : dec %d, hex %02x\n", +- info->f4.DS4IDFMT, info->f4.DS4IDFMT); ++ f4->DS4IDFMT, f4->DS4IDFMT); + printf("DS4HPCHR : %04x%04x%02x " \ + "(cyl %d, trk %d, blk %d)\n", +- info->f4.DS4HPCHR.cc, info->f4.DS4HPCHR.hh, +- info->f4.DS4HPCHR.b, +- vtoc_get_cyl_from_cchhb(&info->f4.DS4HPCHR), +- vtoc_get_head_from_cchhb(&info->f4.DS4HPCHR), +- info->f4.DS4HPCHR.b); ++ f4->DS4HPCHR.cc, f4->DS4HPCHR.hh, ++ f4->DS4HPCHR.b, ++ vtoc_get_cyl_from_cchhb(&f4->DS4HPCHR), ++ vtoc_get_head_from_cchhb(&f4->DS4HPCHR), ++ f4->DS4HPCHR.b); + printf("DS4DSREC : dec %d, hex %04x\n", +- info->f4.DS4DSREC, info->f4.DS4DSREC); ++ f4->DS4DSREC, f4->DS4DSREC); + printf("DS4HCCHH : %04x%04x (cyl %d, trk %d)\n", +- info->f4.DS4HCCHH.cc, info->f4.DS4HCCHH.hh, +- vtoc_get_cyl_from_cchh(&info->f4.DS4HCCHH), +- vtoc_get_head_from_cchh(&info->f4.DS4HCCHH)); ++ f4->DS4HCCHH.cc, f4->DS4HCCHH.hh, ++ vtoc_get_cyl_from_cchh(&f4->DS4HCCHH), ++ vtoc_get_head_from_cchh(&f4->DS4HCCHH)); + printf("DS4NOATK : dec %d, hex %04x\n", +- info->f4.DS4NOATK, info->f4.DS4NOATK); ++ f4->DS4NOATK, f4->DS4NOATK); + printf("DS4VTOCI : dec %d, hex %02x\n", +- info->f4.DS4VTOCI, info->f4.DS4VTOCI); ++ f4->DS4VTOCI, f4->DS4VTOCI); + printf("DS4NOEXT : dec %d, hex %02x\n", +- info->f4.DS4NOEXT, info->f4.DS4NOEXT); ++ f4->DS4NOEXT, f4->DS4NOEXT); + printf("DS4SMSFG : dec %d, hex %02x\n", +- info->f4.DS4SMSFG, info->f4.DS4SMSFG); ++ f4->DS4SMSFG, f4->DS4SMSFG); + printf("DS4DEVAC : dec %d, hex %02x\n", +- info->f4.DS4DEVAC, info->f4.DS4DEVAC); ++ f4->DS4DEVAC, f4->DS4DEVAC); + printf("DS4DSCYL : dec %d, hex %04x\n", +- info->f4.DS4DEVCT.DS4DSCYL, info->f4.DS4DEVCT.DS4DSCYL); ++ f4->DS4DEVCT.DS4DSCYL, f4->DS4DEVCT.DS4DSCYL); + printf("DS4DSTRK : dec %d, hex %04x\n", +- info->f4.DS4DEVCT.DS4DSTRK, info->f4.DS4DEVCT.DS4DSTRK); ++ f4->DS4DEVCT.DS4DSTRK, f4->DS4DEVCT.DS4DSTRK); + printf("DS4DEVTK : dec %d, hex %04x\n", +- info->f4.DS4DEVCT.DS4DEVTK, info->f4.DS4DEVCT.DS4DEVTK); ++ f4->DS4DEVCT.DS4DEVTK, f4->DS4DEVCT.DS4DEVTK); + printf("DS4DEVI : dec %d, hex %02x\n", +- info->f4.DS4DEVCT.DS4DEVI, info->f4.DS4DEVCT.DS4DEVI); ++ f4->DS4DEVCT.DS4DEVI, f4->DS4DEVCT.DS4DEVI); + printf("DS4DEVL : dec %d, hex %02x\n", +- info->f4.DS4DEVCT.DS4DEVL, info->f4.DS4DEVCT.DS4DEVL); ++ f4->DS4DEVCT.DS4DEVL, f4->DS4DEVCT.DS4DEVL); + printf("DS4DEVK : dec %d, hex %02x\n", +- info->f4.DS4DEVCT.DS4DEVK, info->f4.DS4DEVCT.DS4DEVK); ++ f4->DS4DEVCT.DS4DEVK, f4->DS4DEVCT.DS4DEVK); + printf("DS4DEVFG : dec %d, hex %02x\n", +- info->f4.DS4DEVCT.DS4DEVFG, info->f4.DS4DEVCT.DS4DEVFG); ++ f4->DS4DEVCT.DS4DEVFG, f4->DS4DEVCT.DS4DEVFG); + printf("DS4DEVTL : dec %d, hex %04x\n", +- info->f4.DS4DEVCT.DS4DEVTL, info->f4.DS4DEVCT.DS4DEVTL); ++ f4->DS4DEVCT.DS4DEVTL, f4->DS4DEVCT.DS4DEVTL); + printf("DS4DEVDT : dec %d, hex %02x\n", +- info->f4.DS4DEVCT.DS4DEVDT, info->f4.DS4DEVCT.DS4DEVDT); ++ f4->DS4DEVCT.DS4DEVDT, f4->DS4DEVCT.DS4DEVDT); + printf("DS4DEVDB : dec %d, hex %02x\n", +- info->f4.DS4DEVCT.DS4DEVDB, info->f4.DS4DEVCT.DS4DEVDB); ++ f4->DS4DEVCT.DS4DEVDB, f4->DS4DEVCT.DS4DEVDB); + printf("DS4AMTIM : hex "); +- for (i=0; i<8; i++) printf("%02x", info->f4.DS4AMTIM[i]); ++ for (i=0; i<8; i++) printf("%02x", f4->DS4AMTIM[i]); + printf("\nDS4AMCAT : hex "); +- for (i=0; i<3; i++) printf("%02x", info->f4.DS4AMCAT[i]); ++ for (i=0; i<3; i++) printf("%02x", f4->DS4AMCAT[i]); + printf("\nDS4R2TIM : hex "); +- for (i=0; i<8; i++) printf("%02x", info->f4.DS4R2TIM[i]); ++ for (i=0; i<8; i++) printf("%02x", f4->DS4R2TIM[i]); + printf("\nres1 : hex "); +- for (i=0; i<5; i++) printf("%02x", info->f4.res1[i]); ++ for (i=0; i<5; i++) printf("%02x", f4->res1[i]); + printf("\nDS4F6PTR : hex "); +- for (i=0; i<5; i++) printf("%02x", info->f4.DS4F6PTR[i]); ++ for (i=0; i<5; i++) printf("%02x", f4->DS4F6PTR[i]); + printf("\nDS4VTOCE : hex %02x%02x%04x%04x%04x%04x\n", +- info->f4.DS4VTOCE.typeind, info->f4.DS4VTOCE.seqno, +- info->f4.DS4VTOCE.llimit.cc, info->f4.DS4VTOCE.llimit.hh, +- info->f4.DS4VTOCE.ulimit.cc, info->f4.DS4VTOCE.ulimit.hh); ++ f4->DS4VTOCE.typeind, f4->DS4VTOCE.seqno, ++ f4->DS4VTOCE.llimit.cc, f4->DS4VTOCE.llimit.hh, ++ f4->DS4VTOCE.ulimit.cc, f4->DS4VTOCE.ulimit.hh); + printf(" typeind : dec %d, hex %02x\n", +- info->f4.DS4VTOCE.typeind, info->f4.DS4VTOCE.typeind); ++ f4->DS4VTOCE.typeind, f4->DS4VTOCE.typeind); + printf(" seqno : dec %d, hex %02x\n", +- info->f4.DS4VTOCE.seqno, info->f4.DS4VTOCE.seqno); ++ f4->DS4VTOCE.seqno, f4->DS4VTOCE.seqno); + printf(" llimit : hex %04x%04x (cyl %d, trk %d)\n", +- info->f4.DS4VTOCE.llimit.cc, info->f4.DS4VTOCE.llimit.hh, +- vtoc_get_cyl_from_cchh(&info->f4.DS4VTOCE.llimit), +- vtoc_get_head_from_cchh(&info->f4.DS4VTOCE.llimit)); ++ f4->DS4VTOCE.llimit.cc, f4->DS4VTOCE.llimit.hh, ++ vtoc_get_cyl_from_cchh(&f4->DS4VTOCE.llimit), ++ vtoc_get_head_from_cchh(&f4->DS4VTOCE.llimit)); + printf(" ulimit : hex %04x%04x (cyl %d, trk %d)\n", +- info->f4.DS4VTOCE.ulimit.cc, info->f4.DS4VTOCE.ulimit.hh, +- vtoc_get_cyl_from_cchh(&info->f4.DS4VTOCE.ulimit), +- vtoc_get_head_from_cchh(&info->f4.DS4VTOCE.ulimit)); ++ f4->DS4VTOCE.ulimit.cc, f4->DS4VTOCE.ulimit.hh, ++ vtoc_get_cyl_from_cchh(&f4->DS4VTOCE.ulimit), ++ vtoc_get_head_from_cchh(&f4->DS4VTOCE.ulimit)); + printf("res2 : hex "); +- for (i=0; i<10; i++) printf("%02x", info->f4.res2[i]); ++ for (i=0; i<10; i++) printf("%02x", f4->res2[i]); + printf("\nDS4EFLVL : dec %d, hex %02x\n", +- info->f4.DS4EFLVL, info->f4.DS4EFLVL); ++ f4->DS4EFLVL, f4->DS4EFLVL); + printf("DS4EFPTR : hex %04x%04x%02x " \ + "(cyl %d, trk %d, blk %d)\n", +- info->f4.DS4EFPTR.cc, info->f4.DS4EFPTR.hh, +- info->f4.DS4EFPTR.b, +- vtoc_get_cyl_from_cchhb(&info->f4.DS4EFPTR), +- vtoc_get_head_from_cchhb(&info->f4.DS4EFPTR), +- info->f4.DS4EFPTR.b); +- printf("res3 : hex %02x\n", info->f4.res3); ++ f4->DS4EFPTR.cc, f4->DS4EFPTR.hh, ++ f4->DS4EFPTR.b, ++ vtoc_get_cyl_from_cchhb(&f4->DS4EFPTR), ++ vtoc_get_head_from_cchhb(&f4->DS4EFPTR), ++ f4->DS4EFPTR.b); ++ printf("res3 : hex %02x\n", f4->res3); + printf("DS4DCYL : dec %d, hex %08x\n", +- info->f4.DS4DCYL, info->f4.DS4DCYL); ++ f4->DS4DCYL, f4->DS4DCYL); + printf("res4 : hex "); +- for (i=0; i<2; i++) printf("%02x", info->f4.res4[i]); ++ for (i=0; i<2; i++) printf("%02x", f4->res4[i]); + printf("\nDS4DEVF2 : dec %d, hex %02x\n", +- info->f4.DS4DEVF2, info->f4.DS4DEVF2); +- printf("res5 : hex %02x\n", info->f4.res5); ++ f4->DS4DEVF2, f4->DS4DEVF2); ++ printf("res5 : hex %02x\n", f4->res5); + } + +-static void +-dasdview_print_vtoc_f5(dasdview_info_t *info) ++static void dasdview_print_vtoc_f5_raw(format5_label_t *f5) + { + int i; + + printf("\n--- VTOC format 5 label ----------------------" \ + "---------------------------------\n"); + +- if (info->f5c < 1) +- { +- printf("This VTOC doesn't contain a format 5 label.\n"); +- return; +- } +- + printf("key identifier\n DS5KEYID : "); +- for (i=0; i<4; i++) printf("%02x", info->f5.DS5KEYID[i]); ++ for (i=0; i<4; i++) printf("%02x", f5->DS5KEYID[i]); + printf("\nfirst extent description\n"); + printf(" DS5AVEXT : %04x%04x%02x " \ + "(start trk: %d, length: %d cyl, %d trk)\n", +- info->f5.DS5AVEXT.t, info->f5.DS5AVEXT.fc, +- info->f5.DS5AVEXT.ft, info->f5.DS5AVEXT.t, +- info->f5.DS5AVEXT.fc, info->f5.DS5AVEXT.ft); ++ f5->DS5AVEXT.t, f5->DS5AVEXT.fc, ++ f5->DS5AVEXT.ft, f5->DS5AVEXT.t, ++ f5->DS5AVEXT.fc, f5->DS5AVEXT.ft); + printf("next 7 extent descriptions\n"); + for (i=0; i<7; i++) + { + printf(" DS5EXTAV[%d] : %04x%04x%02x " \ + "(start trk: %d, length: %d cyl, %d trk)\n", i+2, +- info->f5.DS5EXTAV[i].t, info->f5.DS5EXTAV[i].fc, +- info->f5.DS5EXTAV[i].ft, info->f5.DS5EXTAV[i].t, +- info->f5.DS5EXTAV[i].fc, info->f5.DS5EXTAV[i].ft); ++ f5->DS5EXTAV[i].t, f5->DS5EXTAV[i].fc, ++ f5->DS5EXTAV[i].ft, f5->DS5EXTAV[i].t, ++ f5->DS5EXTAV[i].fc, f5->DS5EXTAV[i].ft); + } + printf("format identifier\n" \ + " DS5FMTID : dec %d, hex %02x\n", +- info->f5.DS5FMTID, info->f5.DS5FMTID); ++ f5->DS5FMTID, f5->DS5FMTID); + printf("next 18 extent descriptions\n"); + for (i=0; i<18; i++) + { + printf(" DS5MAVET[%d] : %04x%04x%02x " \ + "(start trk: %d, length: %d cyl, %d trk)\n", i+9, +- info->f5.DS5MAVET[i].t, info->f5.DS5MAVET[i].fc, +- info->f5.DS5MAVET[i].ft, info->f5.DS5MAVET[i].t, +- info->f5.DS5MAVET[i].fc, info->f5.DS5MAVET[i].ft); ++ f5->DS5MAVET[i].t, f5->DS5MAVET[i].fc, ++ f5->DS5MAVET[i].ft, f5->DS5MAVET[i].t, ++ f5->DS5MAVET[i].fc, f5->DS5MAVET[i].ft); + } + printf("pointer to next format 5 label\n" \ + " DS5PTRDS : %04x%04x%02x " \ + "(cyl %d, trk %d, blk %d)\n", +- info->f5.DS5PTRDS.cc, info->f5.DS5PTRDS.hh, +- info->f5.DS5PTRDS.b, +- vtoc_get_cyl_from_cchhb(&info->f5.DS5PTRDS), +- vtoc_get_head_from_cchhb(&info->f5.DS5PTRDS), +- info->f5.DS5PTRDS.b); ++ f5->DS5PTRDS.cc, f5->DS5PTRDS.hh, ++ f5->DS5PTRDS.b, ++ vtoc_get_cyl_from_cchhb(&f5->DS5PTRDS), ++ vtoc_get_head_from_cchhb(&f5->DS5PTRDS), ++ f5->DS5PTRDS.b); + } + +-static void +-dasdview_print_vtoc_f7(dasdview_info_t *info) ++static void dasdview_print_vtoc_f7_raw(format7_label_t *f7) + { + int i; + + printf("\n--- VTOC format 7 label ----------------------" \ + "---------------------------------\n"); + +- if (info->f7c < 1) +- { +- printf("This VTOC doesn't contain a format 7 label.\n"); +- return; +- } +- + printf("key identifier\n DS7KEYID : "); +- for (i=0; i<4; i++) printf("%02x", info->f7.DS7KEYID[i]); ++ for (i=0; i<4; i++) printf("%02x", f7->DS7KEYID[i]); + printf("\nfirst 5 extent descriptions\n"); + for (i=0; i<5; i++) + { + printf(" DS7EXTNT[%d] : %08x %08x " \ + "(start trk %d, end trk %d)\n", i+1, +- info->f7.DS7EXTNT[i].a, info->f7.DS7EXTNT[i].b, +- info->f7.DS7EXTNT[i].a, info->f7.DS7EXTNT[i].b); ++ f7->DS7EXTNT[i].a, f7->DS7EXTNT[i].b, ++ f7->DS7EXTNT[i].a, f7->DS7EXTNT[i].b); + } + printf("format identifier\n" \ + " DS7FMTID : dec %d, hex %02x\n", +- info->f7.DS7FMTID, info->f7.DS7FMTID); ++ f7->DS7FMTID, f7->DS7FMTID); + printf("next 11 extent descriptions\n"); + for (i=0; i<11; i++) + { + printf(" DS7ADEXT[%d] : %08x %08x " \ + "(start trk %d, end trk %d)\n", i+6, +- info->f7.DS7ADEXT[i].a, info->f7.DS7ADEXT[i].b, +- info->f7.DS7ADEXT[i].a, info->f7.DS7ADEXT[i].b); ++ f7->DS7ADEXT[i].a, f7->DS7ADEXT[i].b, ++ f7->DS7ADEXT[i].a, f7->DS7ADEXT[i].b); + } + printf("reserved field\n res1 : "); +- for (i=0; i<2; i++) printf("%02x", info->f7.res1[i]); ++ for (i=0; i<2; i++) printf("%02x", f7->res1[i]); + printf("\npointer to next format 7 label\n" \ + " DS7PTRDS : %04x%04x%02x " \ + "(cyl %d, trk %d, blk %d)\n", +- info->f7.DS7PTRDS.cc, info->f7.DS7PTRDS.hh, +- info->f7.DS7PTRDS.b, +- vtoc_get_cyl_from_cchhb(&info->f7.DS7PTRDS), +- vtoc_get_head_from_cchhb(&info->f7.DS7PTRDS), +- info->f7.DS7PTRDS.b); ++ f7->DS7PTRDS.cc, f7->DS7PTRDS.hh, ++ f7->DS7PTRDS.b, ++ vtoc_get_cyl_from_cchhb(&f7->DS7PTRDS), ++ vtoc_get_head_from_cchhb(&f7->DS7PTRDS), ++ f7->DS7PTRDS.b); + } + +-static void +-dasdview_print_vtoc_f9(dasdview_info_t *info) ++static void dasdview_print_vtoc_f9_nohead(format9_label_t *f9) + { + unsigned int i; +- int j; + ++ printf("DS9KEYID : dec %d, hex %02x\n", ++ f9->DS9KEYID, f9->DS9KEYID); ++ printf("DS9SUBTY : dec %d, hex %02x\n", ++ f9->DS9SUBTY, f9->DS9SUBTY); ++ printf("DS9NUMF9 : dec %d, hex %02x\n", ++ f9->DS9NUMF9, f9->DS9NUMF9); ++ ++ printf("res1 : hex "); ++ for (i=0; i < sizeof(f9->res1); i++) { ++ if ((i > 0) && (i % 16 == 0)) ++ printf("\n "); ++ printf("%02x", f9->res1[i]); ++ if ((i+9) % 16 == 0) ++ printf(" "); ++ } ++ printf("\n"); ++ ++ printf("DS9FMTID : dec %d, hex %02x\n", ++ f9->DS9FMTID, f9->DS9FMTID); ++ ++ printf("res2 : hex "); ++ for (i=0; i < sizeof(f9->res2); i++) { ++ if ((i > 0) && (i % 16 == 0)) ++ printf("\n "); ++ printf("%02x", f9->res2[i]); ++ if ((i+9) % 16 == 0) ++ printf(" "); ++ } ++ printf("\n"); ++ printf("pointer to next format 9 label\n" ++ " DS9PTRDS : %04x%04x%02x " ++ "(cyl %d, trk %d, blk %d)\n", ++ f9->DS9PTRDS.cc, f9->DS9PTRDS.hh, ++ f9->DS9PTRDS.b, ++ vtoc_get_cyl_from_cchhb(&f9->DS9PTRDS), ++ vtoc_get_head_from_cchhb(&f9->DS9PTRDS), ++ f9->DS9PTRDS.b); ++} ++ ++static void dasdview_print_vtoc_f9_raw(format9_label_t *f9) ++{ + printf("\n--- VTOC format 9 label ----------------------" \ + "---------------------------------\n"); ++ dasdview_print_vtoc_f9_nohead(f9); ++} ++ ++static void dasdview_print_vtoc_dscb(dasdview_info_t *info, void *dscb) ++{ ++ format1_label_t *tmp = dscb; ++ ++ switch (tmp->DS1FMTID) { ++ case 0x00: ++ break; ++ case 0xf1: ++ if (info->vtoc_f1 || info->vtoc_all) ++ dasdview_print_vtoc_f1_raw(dscb); ++ break; ++ case 0xf3: ++ if (info->vtoc_f3 || info->vtoc_all) ++ dasdview_print_vtoc_f3_raw(dscb); ++ break; ++ case 0xf4: ++ if (info->vtoc_f4 || info->vtoc_all) ++ dasdview_print_vtoc_f4_raw(dscb); ++ break; ++ case 0xf5: ++ if (info->vtoc_f5 || info->vtoc_all) ++ dasdview_print_vtoc_f5_raw(dscb); ++ break; ++ case 0xf7: ++ if (info->vtoc_f7 || info->vtoc_all) ++ dasdview_print_vtoc_f7_raw(dscb); ++ break; ++ case 0xf8: ++ if (info->vtoc_f8 || info->vtoc_all) ++ dasdview_print_vtoc_f8_raw(dscb); ++ break; ++ case 0xf9: ++ if (info->vtoc_f9 || info->vtoc_all) ++ dasdview_print_vtoc_f9_raw(dscb); ++ break; ++ default: ++ printf("unrecognized DSCB of type: %x \n\n", tmp->DS1FMTID); ++ break; ++ } ++} ++ ++static void dasdview_print_vtoc_f1(dasdview_info_t *info) ++{ ++ int j; ++ ++ printf("--- VTOC format 1 labels ----------------------" ++ "---------------------------------\n"); ++ ++ if (info->f1c < 1) ++ { ++ printf("This VTOC doesn't contain a format 1 label.\n"); ++ return; ++ } ++ ++ for (j=0; jf1c; j++) { ++ printf("\n--- format 1 DSCB number %d ---\n", j+1); ++ dasdview_print_format1_8_no_head(&info->f1[j]); ++ } ++} ++ ++static void dasdview_print_vtoc_f8(dasdview_info_t *info) ++{ ++ int j; ++ ++ printf("--- VTOC format 8 labels ----------------------" ++ "---------------------------------\n"); ++ ++ if (info->f8c < 1) ++ { ++ printf("This VTOC doesn't contain a format 8 label.\n"); ++ return; ++ } ++ ++ for (j=0; jf8c; j++) { ++ printf("\n--- format 8 DSCB number %d ---\n", j+1); ++ dasdview_print_format1_8_no_head(&info->f8[j]); ++ } ++} ++ ++static void dasdview_print_vtoc_f4(dasdview_info_t *info) ++{ ++ if (info->f4c < 1) { ++ printf("\n--- VTOC format 4 label ----------------------" \ ++ "---------------------------------\n"); ++ printf("This VTOC doesn't contain a format 4 label.\n"); ++ return; ++ } ++ dasdview_print_vtoc_f4_raw(&info->f4); ++} ++ ++static void dasdview_print_vtoc_f5(dasdview_info_t *info) ++{ ++ if (info->f5c < 1) ++ { ++ printf("\n--- VTOC format 5 label ----------------------" \ ++ "---------------------------------\n"); ++ printf("This VTOC doesn't contain a format 5 label.\n"); ++ return; ++ } ++ dasdview_print_vtoc_f5_raw(&info->f5); ++} ++ ++static void dasdview_print_vtoc_f7(dasdview_info_t *info) ++{ ++ if (info->f7c < 1) ++ { ++ printf("\n--- VTOC format 7 label ----------------------" ++ "---------------------------------\n"); ++ printf("This VTOC doesn't contain a format 7 label.\n"); ++ return; ++ } ++ dasdview_print_vtoc_f7_raw(&info->f7); ++} ++ ++static void dasdview_print_vtoc_f9(dasdview_info_t *info) ++{ ++ int j; + ++ printf("\n--- VTOC format 9 label ----------------------" ++ "---------------------------------\n"); + if (info->f9c < 1) { + printf("This VTOC doesn't contain a format 9 label.\n"); + return; + } +- +- for (j=0; jf9c; j++) { ++ for (j = 0; j < info->f9c; j++) { + printf("\n--- format 9 DSCB number %d ---\n", j+1); +- printf("DS9KEYID : dec %d, hex %02x\n", +- info->f9[j].DS9KEYID, info->f9[j].DS9KEYID); +- printf("DS9SUBTY : dec %d, hex %02x\n", +- info->f9[j].DS9SUBTY, info->f9[j].DS9SUBTY); +- printf("DS9NUMF9 : dec %d, hex %02x\n", +- info->f9[j].DS9NUMF9, info->f9[j].DS9NUMF9); +- +- printf("res1 : hex "); +- for (i=0; i < sizeof(info->f9[j].res1); i++) { +- if ((i > 0) && (i % 16 == 0)) +- printf("\n "); +- printf("%02x", info->f9[j].res1[i]); +- if ((i+9) % 16 == 0) +- printf(" "); +- } +- printf("\n"); ++ dasdview_print_vtoc_f9_nohead(&info->f9[j]); ++ } ++} + +- printf("DS9FMTID : dec %d, hex %02x\n", +- info->f9[j].DS9FMTID, info->f9[j].DS9FMTID); ++static void dasdview_print_vtoc_f3(dasdview_info_t *info) ++{ ++ /* dasdfmt formatted DASD devices have no format3 labels, but since the ++ * option exists for raw DASDs, we need to have some sensible message ++ */ ++ printf("\n--- VTOC format 3 label ----------------------" \ ++ "---------------------------------\n"); ++ printf("This VTOC doesn't contain a format 3 label.\n"); ++ return; ++} + +- printf("res2 : hex "); +- for (i=0; i < sizeof(info->f9[j].res2); i++) { +- if ((i > 0) && (i % 16 == 0)) +- printf("\n "); +- printf("%02x", info->f9[j].res2[i]); +- if ((i+9) % 16 == 0) +- printf(" "); +- } +- printf("\n"); ++static void dasdview_print_vtoc_standard(dasdview_info_t *info) ++{ ++ dasdview_read_vtoc(info); ++ ++ if (info->vtoc_info || info->vtoc_all) ++ dasdview_print_vtoc_info(info); ++ ++ if (info->vtoc_f4 || info->vtoc_all) ++ dasdview_print_vtoc_f4(info); ++ ++ if (info->vtoc_f5 || info->vtoc_all) ++ dasdview_print_vtoc_f5(info); ++ ++ if (info->vtoc_f7 || info->vtoc_all) ++ dasdview_print_vtoc_f7(info); ++ ++ if (info->vtoc_f1 || info->vtoc_all) ++ dasdview_print_vtoc_f1(info); ++ ++ if (info->vtoc_f8 || info->vtoc_all) ++ dasdview_print_vtoc_f8(info); ++ ++ if (info->vtoc_f9 || info->vtoc_all) ++ dasdview_print_vtoc_f9(info); ++ ++ if (info->vtoc_f3 || info->vtoc_all) ++ dasdview_print_vtoc_f3(info); ++} ++ ++/* a simple routine to print all records in the vtoc */ ++static void dasdview_print_vtoc_raw(dasdview_info_t *info) ++{ ++ struct dscbiterator *it; ++ struct dscb *record; ++ int rc; ++ ++ rc = lzds_dasd_read_vlabel(info->dasd); ++ if (rc) { ++ zt_error_print("error when reading label from device:" ++ " rc=%d\n", rc); ++ exit(-1); ++ } ++ rc = lzds_dasd_read_rawvtoc(info->dasd); ++ if (rc == EINVAL) { ++ zt_error_print("dasdview: Cannot read VTOC because disk does" ++ " not contain valid VOL1 label.\n", ++ info->device); ++ exit(-1); ++ } else if (rc) { ++ zt_error_print("error when reading vtoc from device:" ++ " rc=%d\n", rc); ++ exit(-1); ++ } ++ rc = lzds_dasd_get_rawvtoc(info->dasd, &info->rawvtoc); ++ if (rc || !info->rawvtoc) { ++ zt_error_print("dasdview: libvtoc could not read vtoc\n"); ++ exit(-1); ++ } ++ ++ if (info->vtoc_info || info->vtoc_all) ++ dasdview_print_vtoc_info_raw(info); ++ ++ rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); ++ if (rc) { ++ zt_error_print("dasdview: could not allocate DSCB iterator\n"); ++ exit(-1); + } ++ while (!lzds_dscbiterator_get_next_dscb(it, &record)) ++ dasdview_print_vtoc_dscb(info, record); ++ lzds_dscbiterator_free(it); ++} ++ ++static void dasdview_print_vtoc(dasdview_info_t *info) ++{ ++ if (info->raw_track_access) ++ dasdview_print_vtoc_raw(info); ++ else ++ dasdview_print_vtoc_standard(info); + } + + static int +@@ -1333,8 +1919,7 @@ dasdview_print_format2(unsigned int size, unsigned char *dumpstr, + return 0; + } + +-static int +-dasdview_view(dasdview_info_t *info) ++static void dasdview_view_standard(dasdview_info_t *info) + { + unsigned char dumpstr[DUMP_STRING_SIZE]; + unsigned long long i=0, j=0, k=0, count=0; +@@ -1478,7 +2063,209 @@ dasdview_view(dasdview_info_t *info) + "------+----------+----------+\n\n"); + } + +- return 0; ++} ++ ++static void dasdview_print_format_raw(unsigned int size, char *dumpstr) ++{ ++ unsigned int i; ++ char asc[17], ebc[17]; ++ unsigned int residual, count; ++ char *data; ++ ++ data = dumpstr; ++ residual = size; ++ while (residual) { ++ /* we handle at most 16 bytes per line */ ++ count = min(residual, 16); ++ bzero(asc, 17); ++ bzero(ebc, 17); ++ printf("|"); ++ memcpy(asc, data, count); ++ memcpy(ebc, data, count); ++ ++ for (i = 0; i < 16; ++i) { ++ if ((i % 4) == 0) ++ printf(" "); ++ if ((i % 8) == 0) ++ printf(" "); ++ if (i < count) ++ printf("%02X", data[i]); ++ else ++ printf(" "); ++ } ++ vtoc_ebcdic_dec(asc, asc, count); ++ dot(asc); ++ dot(ebc); ++ printf(" | %16.16s | %16.16s |\n", asc, ebc); ++ data += count; ++ residual -= count; ++ } ++} ++ ++/* gets the pointer to an eckd record structure in memory and ++ * prints a hex/ascii/ebcdic dump for it ++ */ ++static void dasdview_print_raw_record(char *rec) ++{ ++ ++ struct eckd_count *ecount; ++ unsigned int cyl, head; ++ ++ ecount = (struct eckd_count *)rec; ++ /* Note: the first 5 bytes of the count area are the ++ * record ID and by convention these bytes are interpreted ++ * as CCHHR (or ccccCCChR for large volumes) ++ */ ++ cyl = vtoc_get_cyl_from_cchhb(&ecount->recid); ++ head = vtoc_get_head_from_cchhb(&ecount->recid); ++ printf("+-----------------------------------------" ++ "-------------------------------------+\n"); ++ printf("| count area: " ++ " |\n"); ++ printf("| hex: %016llX " ++ " |\n", ++ *((unsigned long long *)ecount)); ++ printf("| cylinder: %9d " ++ " |\n", cyl); ++ printf("| head: %9d " ++ " |\n", head); ++ printf("| record: %9d " ++ " |\n", ecount->recid.b); ++ printf("| key length: %9d " ++ " |\n", ecount->kl); ++ printf("| data length: %9d " ++ " |\n", ecount->dl); ++ printf("+-----------------------------------------" ++ "-------------------------------------+\n"); ++ printf("| key area: " ++ " |\n"); ++ printf("| HEXADECIMAL |" ++ " EBCDIC | ASCII |\n"); ++ printf("| 01....04 05....08 09....12 13....16 |" ++ " 1.............16 | 1.............16 |\n"); ++ printf("+----------------------------------------+" ++ "------------------+------------------+\n"); ++ dasdview_print_format_raw(ecount->kl, rec + sizeof(*ecount)); ++ printf("+----------------------------------------+" ++ "------------------+------------------+\n"); ++ printf("| data area: " ++ " |\n"); ++ printf("| HEXADECIMAL |" ++ " EBCDIC | ASCII |\n"); ++ printf("| 01....04 05....08 09....12 13....16 |" ++ " 1.............16 | 1.............16 |\n"); ++ printf("+----------------------------------------+" ++ "------------------+------------------+\n"); ++ dasdview_print_format_raw(ecount->dl, ++ rec + sizeof(*ecount) + ecount->kl); ++ printf("+----------------------------------------+" ++ "------------------+------------------+\n"); ++} ++ ++static void dasdview_print_raw_track(char *trackdata, ++ unsigned int cyl, ++ unsigned int head) ++{ ++ struct eckd_count *ecount; ++ char *data; ++ u_int32_t record; ++ ++ record = 0; ++ data = trackdata; ++ ++ do { ++ printf("cylinder %u, head %u, record %u\n", ++ cyl, head, record); ++ dasdview_print_raw_record(data); ++ printf("\n"); ++ ++ ecount = (struct eckd_count *)data; ++ data += sizeof(*ecount) + ecount->kl + ecount->dl; ++ ++record; ++ ++ if ((*(unsigned long long *)data) == ENDTOKEN) { ++ break; ++ } ++ if ((unsigned long)data >= ++ (unsigned long)trackdata + RAWTRACKSIZE) { ++ break; ++ } ++ } while (1); ++} ++ ++static void dasdview_view_raw(dasdview_info_t *info) ++{ ++ u_int64_t residual, trckstart, trckend, track, trckbuffsize; ++ u_int64_t tracks_to_read, trckcount, i; ++ char *trackdata; ++ char *data; ++ int rc; ++ struct dasdhandle *dasdh; ++ ++ trckstart = info->begin / RAWTRACKSIZE; ++ tracks_to_read = info->size / RAWTRACKSIZE; ++ ++ /* TODO: how large should we make our buffer? ++ * The DASD device driver cannot read more than 16 tracks at once ++ * but we can read a larger blob and the block layer will split up ++ * the requests for us. ++ */ ++ trckbuffsize = min(tracks_to_read, 16); ++ /* track data must be page aligned for O_DIRECT */ ++ trackdata = memalign(4096, trckbuffsize * RAWTRACKSIZE); ++ if (!trackdata) { ++ zt_error_print("failed to allocate memory\n"); ++ exit(-1); ++ } ++ rc = lzds_dasd_alloc_dasdhandle(info->dasd, &dasdh); ++ if (rc) { ++ zt_error_print("failed to allocate memory\n"); ++ exit(-1); ++ } ++ rc = lzds_dasdhandle_open(dasdh); ++ if (rc) { ++ lzds_dasdhandle_free(dasdh); ++ zt_error_print("failed to open device\n"); ++ exit(-1); ++ } ++ /* residual is the number of tracks we still have to read */ ++ residual = tracks_to_read; ++ track = trckstart; ++ while (residual) { ++ trckcount = min(trckbuffsize, residual); ++ trckend = track + trckcount - 1; ++ rc = lzds_dasdhandle_read_tracks_to_buffer(dasdh, track, ++ trckend, trackdata); ++ if (rc) { ++ perror("Error on read"); ++ exit(-1); ++ } ++ data = trackdata; ++ for (i = 0; i < trckcount; ++i) { ++ dasdview_print_raw_track(data, track / info->geo.heads, ++ track % info->geo.heads); ++ data += RAWTRACKSIZE; ++ ++track; ++ } ++ residual -= trckcount; ++ } ++ ++ free(trackdata); ++ ++ rc = lzds_dasdhandle_close(dasdh); ++ lzds_dasdhandle_free(dasdh); ++ if (rc < 0) { ++ perror("Error on closing file"); ++ exit(-1); ++ } ++} ++ ++static void dasdview_view(dasdview_info_t *info) ++{ ++ if (info->raw_track_access) ++ dasdview_view_raw(info); ++ else ++ dasdview_view_standard(info); + } + + static void +@@ -1502,6 +2289,7 @@ int main(int argc, char * argv[]) { + char *begin_param_str = NULL; + char *size_param_str = NULL; + char *endptr = NULL; ++ int rc; + + bzero (&info, sizeof(info)); + while (1) +@@ -1559,27 +2347,28 @@ int main(int argc, char * argv[]) { + info.volser = 1; + break; + case 't': +- if (strncmp(optarg,"info",4)==0) ++ if (strcmp(optarg, "info")==0) + info.vtoc_info = 1; +- else if (strncmp(optarg,"f1",2)==0) ++ else if (strcmp(optarg, "f1")==0) + info.vtoc_f1 = 1; +- else if (strncmp(optarg,"f4",2)==0) ++ else if (strcmp(optarg, "f3")==0) ++ info.vtoc_f3 = 1; ++ else if (strcmp(optarg, "f4")==0) + info.vtoc_f4 = 1; +- else if (strncmp(optarg,"f5",2)==0) ++ else if (strcmp(optarg, "f5")==0) + info.vtoc_f5 = 1; +- else if (strncmp(optarg,"f7",2)==0) ++ else if (strcmp(optarg, "f7")==0) + info.vtoc_f7 = 1; +- else if (strncmp(optarg,"f8",2)==0) ++ else if (strcmp(optarg, "f8")==0) + info.vtoc_f8 = 1; +- else if (strncmp(optarg,"f9",2)==0) ++ else if (strcmp(optarg, "f9")==0) + info.vtoc_f9 = 1; +- else if (strncmp(optarg,"all",3)==0) ++ else if (strcmp(optarg, "all")==0) + info.vtoc_all = 1; +- else +- { +- zt_error_print("dasdview: usage error\n" \ +- "%s is no valid option!\n", +- optarg); ++ else { ++ zt_error_print("dasdview: usage error\n" ++ "%s is no valid argument for" ++ " option -t/--vtoc\n", optarg); + exit(-1); + } + info.vtoc = 1; +@@ -1646,19 +2435,34 @@ int main(int argc, char * argv[]) { + } + + dasdview_get_info(&info); ++ if (info.raw_track_access) { ++ rc = lzds_zdsroot_alloc(&info.zdsroot); ++ if (rc) { ++ zt_error_print("Could not allocate index\n"); ++ exit(-1); ++ } ++ rc = lzds_zdsroot_add_device(info.zdsroot, info.device, ++ &info.dasd); ++ if (rc) { ++ zt_error_print("Could not add device to index\n"); ++ exit(-1); ++ } ++ } + + if (info.begin_specified) +- { +- + dasdview_parse_input(&info.begin, &info, begin_param_str); +- } + else + info.begin = DEFAULT_BEGIN; + +- max = (unsigned long long) info.hw_cylinders * +- (unsigned long long) info.geo.heads * +- (unsigned long long) info.geo.sectors * +- (unsigned long long) info.blksize; ++ if (info.raw_track_access) { ++ max = (unsigned long long) info.hw_cylinders * ++ (unsigned long long) info.geo.heads * RAWTRACKSIZE; ++ } else ++ max = (unsigned long long) info.hw_cylinders * ++ (unsigned long long) info.geo.heads * ++ (unsigned long long) info.geo.sectors * ++ (unsigned long long) info.blksize; ++ + if (info.begin > max) + { + zt_error_print("dasdview: usage error\n" \ +@@ -1667,9 +2471,9 @@ int main(int argc, char * argv[]) { + } + + if (info.size_specified) +- { + dasdview_parse_input(&info.size, &info, size_param_str); +- } ++ else if (info.raw_track_access) ++ info.size = RAWTRACKSIZE; + else + info.size = DEFAULT_SIZE; + +@@ -1715,44 +2519,8 @@ int main(int argc, char * argv[]) { + dasdview_print_vlabel(&info); + + if (info.vtoc) +- { +- dasdview_read_vtoc(&info); +- } +- +- if (info.vtoc_info || info.vtoc_all) +- { +- dasdview_print_vtoc_info(&info); +- } +- +- if (info.vtoc_f4 || info.vtoc_all) +- { +- dasdview_print_vtoc_f4(&info); +- } +- +- if (info.vtoc_f5 || info.vtoc_all) +- { +- dasdview_print_vtoc_f5(&info); +- } +- +- if (info.vtoc_f7 || info.vtoc_all) +- { +- dasdview_print_vtoc_f7(&info); +- } +- +- if (info.vtoc_f1 || info.vtoc_all) +- { +- dasdview_print_vtoc_f1(&info); +- } +- +- if (info.vtoc_f8 || info.vtoc_all) +- { +- dasdview_print_vtoc_f8(&info); +- } ++ dasdview_print_vtoc(&info); + +- if (info.vtoc_f9 || info.vtoc_all) +- { +- dasdview_print_vtoc_f9(&info); +- } + + if (!info.action_specified) + { +diff --git a/dasdview/dasdview.h b/dasdview/dasdview.h +index 5388592..3c17619 100644 +--- a/dasdview/dasdview.h ++++ b/dasdview/dasdview.h +@@ -1,13 +1,14 @@ + /* + * File...........: s390-tools/dasdview/dasdview.h + * Author(s)......: Horst Hummel +- * Copyright IBM Corp. 2002, 2006. ++ * Copyright IBM Corp. 2002, 2013. + */ + + #ifndef DASDVIEW_H + #define DASDVIEW_H + + #include ++#include "u2s.h" + + /******************************************************************************** + * SECTION: Definitions needed for DASD-API (see dasd.h) +@@ -241,6 +242,7 @@ typedef struct dasdview_info + int vtoc; + int vtoc_info; + int vtoc_f1; ++ int vtoc_f3; + int vtoc_f4; + int vtoc_f5; + int vtoc_f7; +@@ -261,6 +263,14 @@ typedef struct dasdview_info + int f7c; + int f8c; + int f9c; ++ ++ char busid[U2S_BUS_ID_SIZE]; ++ int busid_valid; ++ int raw_track_access; ++ struct zdsroot *zdsroot; ++ struct raw_vtoc *rawvtoc; ++ struct dasd *dasd; ++ + } dasdview_info_t; + + +diff --git a/include/libzds.h b/include/libzds.h +new file mode 100644 +index 0000000..c4bebe0 +--- /dev/null ++++ b/include/libzds.h +@@ -0,0 +1,812 @@ ++/** ++ * \file libzds.h ++ * This is the main header file for the internal library libzds. ++ * Please note that this library should currently only be used ++ * by programs in the s390-tools package. It is not yet meant ++ * for external use as interfaces and definitions may change ++ * without further notice. ++ * ++ * Copyright IBM Corporation 2013 ++ */ ++ ++/** ++ * @mainpage ++ * The libzds is a s390-tools internal library for use with DASD ++ * devices in raw_track_access mode. ++ * ++ * The regular operation mode of the DASD device driver allows only to ++ * access ECKD DASDs that were formatted with a specific record ++ * layout. The raw access mode of the DASD device driver allows to ++ * access any kind of ECKD DASD, but requires the correct use of ++ * the DIRECT_IO interface and leaves the interpretation of the data ++ * format on these devices to the user. ++ * ++ * This library supports the use of raw DASD devices by providing ++ * functions that ++ * @li access the device with DIRECT_IO and the correct buffer alignment ++ * @li provide access to label and VTOC data on the device ++ * @li provide access to simple z/OS data set formats ++ * (physical sequential (PS) and partitioned data sets (PDS)) ++ * ++ * ++ * @section interface_groups Library Interface ++ * ++ * @subsection interface_structures Data Structures ++ * ++ * The data structures provided by this library can be devided into ++ * two types: Structures that represent external hardware and software ++ * interfaces, and structures that are defined by libzds itself: ++ * ++ * @ref external_interfaces ++ * ++ * @ref libzds_data ++ * ++ * ++ * @subsection interface_functions Functions ++ * ++ * The functions provided by this library are devided into 5 categories: ++ * base, low, mid, and highlevel functions and helper functions. ++ * ++ * The lower the level, the less dependent are the functions on the data ++ * that is stored on the DASDs. The higher the level the more abstract ++ * are the implemented concepts. ++ * ++ * The base level functions are needed to setup the internal data ++ * structures which the other functions work on. Otherwise, he use of higher ++ * level functions does not require the use of low level functions. ++ * For example: To simply read data from a data set, you just need the ++ * base and high level functions and can ignore the low and mid level ++ * functions. ++ * ++ * \ref libzds_functions_base ++ * ++ * \ref libzds_functions_low ++ * ++ * \ref libzds_functions_mid ++ * ++ * \ref libzds_functions_high ++ * ++ * \ref libzds_functions_helper ++ * ++ * ++ * @section naming_scheme Naming Scheme ++ * ++ * All interface functions start with lzds_ for libzds (could be changed later ++ * but libzds_ is quite long). Next is the entity the function works on, ++ * for example ++ * @li @c lzds_zdsroot_... ++ * @li @c lzds_dasd_... ++ * ++ * So if you know what entity you want to work on, you know where to look. ++ * ++ * Then follows the operation (add, get, read, alloc, ...). ++ * There are several verbs that can mean 'access data'. ++ * As a guideline we define the following meaning for use in this library: ++ * @li read: Will result in data being read from a device ++ * @li get: Get a value from one of the internal structures ++ * @li extract: Use the internal data to create higher level data, ++ * e.g. use the VTOC information of a DASD to create data ++ * set structures ++ * @li alloc: Create and return a libzds data structure ++ * ++ * @note For every alloc function there shall be a matching free function to ++ * release the memory. However, often the structure is created in the ++ * context of a another structure, but when it is freed, that is done in ++ * its own context. For example: ++ * lzds_zdsroot_alloc_dasditerator is matched by lzds_dasditerator_free ++ * ++ * Finally the object of the operation, what you want to get or achieve, ++ * e.g lzds_ds_get_is_PDS ++ * ++ * For the parmeter list, the general rule is: ++ * The subject comes first, the object last, further parameters in between. ++ */ ++ ++ ++#ifndef LIBZDS_H ++/** ++ * @brief Watchdog for libzds.h inclusion. ++ */ ++#define LIBZDS_H ++ ++#include "vtoc.h" ++ ++ ++ ++/** ++ * \defgroup external_interfaces External constants and structures. ++ * @{ ++ * @brief These constants and structures are related to ++ * hardware and sofware interfaces that are specified outside of ++ * libzds. ++ * ++ * @li For a description of ECKD data formats see ++ * 'IBM 3990/9390 Storage Control Reference', Document Number GA32-0274-05 ++ * @li For a description of VTOC entries see ++ * 'z/OS DFSMSdfp Advanced Services', Document Number SC26-7400-11 ++ * @li For a description of physical sequential and partitioned data sets see ++ * 'z/OS DFSMS Using Data Sets', Document Number SC26-7410-11 ++ * @li For a description of the Linux on System z DASD device driver see ++ * 'Device Drivers, Features, and Commands (kernel 3.7)', ++ * Document Number SC33-8411-18 ++ */ ++ ++ ++/** ++ * @brief The size of one raw track when read via the DASD device driver ++ * with raw_track_access. ++ * ++ * When reading from a DASD in raw_track_access mode, you need to ++ * align your I/O to multiples of this size. ++ */ ++#define RAWTRACKSIZE 65536 ++ ++/** ++ * @brief Maximum size of one record on a track ++ * ++ * This is the maximum size of a single record on a track. If a track contains ++ * multiple records, the additional overhead will cause the sum of these ++ * multiple records to be smaller than the biggest single record, so MAXRECSIZE ++ * is also the upper limit for user data that a single track can hold. ++ */ ++#define MAXRECSIZE 56664 ++ ++ ++/** ++ * @brief Maximum number of extents a data set can hold. ++ * ++ * We do not handle extended format data sets so we can have a total of 16 ++ * extents per dataset (3 in the f1 and 13 in the f3 label). ++ */ ++#define MAXEXTENTS 16 ++ ++ ++/** ++ * @brief Maximum size of a data set name string (including one byte for ++ * 0-termination) ++ */ ++#define MAXDSNAMELENGTH 45 ++ ++/** ++ * @brief Maximum size of a partitioned data set member name string ++ * (including one byte for 0-termination) ++ */ ++#define MEMBERNAMELENGTH 9 ++ ++/** ++ * @brief The maximum number of volumes (devices) that a multi volume data ++ * set can span. ++ */ ++#define MAXVOLUMESPERDS 59 ++ ++/** ++ * @brief Eight bytes of 0xFF are used in several cases to designate the end of data. ++ * ++ */ ++#define ENDTOKEN 0xFFFFFFFFFFFFFFFFULL ++ ++ ++ ++/** ++ * @brief This structure represents the count field in an ECKD record. ++ */ ++struct eckd_count { ++ /** @brief record ID ++ * ++ * In general the record ID is defined as just a 5 byte field. ++ * The interpretation of these 5 bytes as a struct cchhb_t is a common ++ * convention, which we assume here as well. ++ */ ++ cchhb_t recid; ++ /** @brief key length */ ++ unsigned char kl; ++ /** @brief data length */ ++ unsigned short dl; ++} __attribute__ ((packed)); ++ ++/** ++ * @brief A generic structure to describe a data set control block (DSCB) ++ * ++ * The elements of the VTOC are called DSCBs. All DSCBs have in common that ++ * they have a size of 140 bytes, and byte 44 is the identifier that ++ * determines the type of DSCB. ++ */ ++struct dscb { ++ /** @brief key area ++ * ++ * This part of the DSCB is usually stored in the key part of an ++ * ECKD record in the VTOC. The contents depends on the format. ++ */ ++ char key[44]; ++ /** @brief Format identifier ++ * ++ * This identifier determines the data layout of the rest of the ++ * DSCB. In a format-x DSCB this field is called DSxFMTID. ++ * The identifiers for format-1 to format-9 are the respective ++ * EBCDIC characters '1' to '9' (0xf1 to 0xf9). ++ * An empty DSCB record (format-0 DSCB) contains 140 zeros, so ++ * here the format id is 0x00. ++ */ ++ char fmtid; ++ /** @brief The residual data part of the DSCB ++ * ++ * The contents depends on the format. ++ */ ++ char data[95]; ++} __attribute__ ((packed)); ++ ++ ++/** ++ * @brief This structure represents a segment descriptor word (SDW), ++ * record descriptor word (RDW) or block descriptor word (BDW). ++ * ++ * These are used to describe data set blocks and records. ++ * (see z/OS DFSMS Using Data Sets) ++ */ ++struct segment_header { ++ /** @brief Is this an empty segment (valid for SDW) */ ++ unsigned short nullsegment:1; ++ /** @brief Length of the segment */ ++ unsigned short length:15; ++ /** @brief reserved */ ++ unsigned char reserved1:6; ++ /** @brief Segment control code (valid for SDW) ++ * ++ * 0: logical record consists of just this segment, ++ * 1: first segment in the logical record, ++ * 2: last segment in the logical record, ++ * 3: intermediate in the logical record ++ */ ++ unsigned char position:2; ++ /** @brief reserved */ ++ unsigned char reserved3:8; ++} __attribute__ ((packed)); ++ ++ ++ ++/** ++ * @brief The key length of a PDS directory member record. ++ */ ++#define PDS_DIR_KL 8 ++/** ++ * @brief The data length of a PDS directory member record. ++ */ ++#define PDS_DIR_DL 256 ++ ++/** ++ * @brief This structure represents and entry in the PDS directory and ++ * describes a member of the data set. ++ * ++ * This structure represents only the fixed part of the member ++ * entry. The variable size of the user data part is determined ++ * by the entry user_data_count. ++ * (see z/OS DFSMS Using Data Sets) ++ */ ++struct pds_member_entry { ++ /** @brief Member name */ ++ char name[8]; ++ /** @brief Start track of the member (relative to start of the PDS) */ ++ unsigned short track; ++ /** @brief Start record of the member */ ++ unsigned char record; ++ /** @brief Is this entry an alias for another entry? */ ++ unsigned char is_alias:1; ++ /** @brief How many TTRN note lists are contained in the user data. */ ++ unsigned char ttrn_count:2; ++ /** @brief The user_data_count value counts 'half words' i.e. shorts! */ ++ unsigned char user_data_count:5; ++} __attribute__ ((packed)); ++ ++ ++/** @} */ /* end of group hardware */ ++ ++ ++ ++/** ++ * @defgroup libzds_data libzds data structures ++ * @{ ++ * @brief These are the data structures used by the libzds API. ++ * ++ * For users of libzds these are opaque data structures and they have ++ * no dependency on the implementation details of these structures. ++ * All libzds interface functions work on pointers to these structures, ++ * so programs that use the library do not need to know them either. ++ * This prevents users from accessing the data in unsupported ways ++ * allows us to change the implementation without changing the ++ * interface. ++ */ ++ ++/** ++ * @struct zdsroot ++ * @brief The root of all device and data set information. ++ * ++ * Note that data sets do not belong to DASDs, as they ++ * may span over more than one DASD. ++ */ ++struct zdsroot; ++ ++/** ++ * @struct dasd ++ * @brief Represents one physical device, may have a vtoc ++ */ ++struct dasd; ++ ++/** ++ * @struct dasditerator ++ * @brief Allows to iterate over all dasds in the zdsroot ++ */ ++struct dasditerator; ++ ++/** ++ * @struct dasdhandle ++ * @brief Represents the state of a DASD device while it is in use. ++ * ++ * For applications that need to read data directly from a DASD device. ++ * The idea is to have an abstract handle for a DASD that is in ++ * use, similar to a FILE pointer ++ */ ++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 ++ */ ++struct dscbiterator; ++ ++/** ++ * @struct dataset ++ * @brief The whole of one data set ++ * ++ * May refer to one or more dataset parts ++ * and may have a list of partitioned dataset members. ++ */ ++struct dataset; ++ ++/** ++ * @struct dsiterator ++ * @brief Allows to iterate over all data sets in the zdsroot ++ */ ++struct dsiterator; ++ ++/** ++ * @struct pdsmember ++ * @brief If a data set is a partitioned data set (PDS) it ++ * may have zero or more PDS members ++ */ ++struct pdsmember; ++ ++/** ++ * @struct memberiterator ++ * @brief Allows to iterate over all members in the dataset ++ */ ++struct memberiterator; ++ ++/** ++ * @struct dshandle ++ * @brief Represents the state of a data set while it is in use ++ * ++ * This state includes the I/O buffers used for reading, ++ * the position within the data set, options used for processing the data, ++ * etc. The idea is to have an abstract handle for a data set that is in ++ * use, similar to a FILE pointer. ++ */ ++struct dshandle; ++ ++/** ++ * @struct error_log ++ * @brief A stack of error messages that are related to the last error ++ */ ++struct errorlog; ++ ++/** @} */ /* end of group libzds_data */ ++ ++ ++ ++ ++/** ++ * @defgroup libzds_functions_base Base functions ++ * @{ ++ * @brief These functions are basic setup functions which need to ++ * be used before any of the low, mid or high level functions ++ * can be used. These functions concern the allocation and ++ * initialization of the zdsroot and the basic handling of devices. ++ */ ++ ++/** ++ * @brief Allocate a new zdsroot structure. ++ */ ++int lzds_zdsroot_alloc(struct zdsroot **root); ++ ++/** ++ * @brief Free the memory of the given zdsroot structure. ++ */ ++void lzds_zdsroot_free(struct zdsroot *root); ++ ++/** ++ * @brief Add a DASD device to the zdsroot. ++ */ ++int lzds_zdsroot_add_device(struct zdsroot *root, const char *devnode, ++ struct dasd **dasd); ++/** ++ * @brief Get the errorlog. ++ */ ++void lzds_dasd_get_errorlog(struct dasd *dasd, struct errorlog **log); ++ ++/** ++ * @brief Allocate index that allows to iterate through all DASDs ++ * stored in the root. ++ */ ++int lzds_zdsroot_alloc_dasditerator(struct zdsroot *root, ++ struct dasditerator **it); ++/** ++ * @brief Free the dasditerator structure. ++ */ ++void lzds_dasditerator_free(struct dasditerator *it); ++ ++/** ++ * @brief Get the next dasd structure. ++ */ ++int lzds_dasditerator_get_next_dasd(struct dasditerator *it, ++ struct dasd **dasd); ++ ++/** ++ * @brief Return the device node name that was used for this dasd. ++ */ ++void lzds_dasd_get_device(struct dasd *dasd, char **device); ++ ++/** ++ * @brief Get the dasd structure that belongs to the given device. ++ */ ++int lzds_zdsroot_get_dasd_by_node_name(struct zdsroot *root, const char *device, ++ struct dasd **dasd); ++ ++/** ++ * @brief Get the errorlog. ++ */ ++void lzds_zdsroot_get_errorlog(struct zdsroot *root, struct errorlog **log); ++ ++int lzds_errorlog_fprint(struct errorlog *log, FILE *stream); ++ ++/** @} */ /* end of group libzds_functions_base */ ++ ++ ++ ++ ++ ++/** ++ * @defgroup libzds_functions_low Low level interface functions. ++ * @{ ++ * @brief Very basic functions, should all work on every kind DASD. ++ * ++ * These functions give access to the data on a DASD on a very ++ * low abstraction level. They do not dependent on the data on the ++ * device itself. ++ * ++ */ ++ ++/** ++ * @brief Based on the dasd device geometry, compute a track number from a ++ * given cchh_t (cylinder, head) address value. ++ */ ++void lzds_dasd_cchh2trk(struct dasd *dasd, cchh_t *p, unsigned int *track); ++ ++/** ++ * @brief Get the number of cylinders that this DASD has. ++ */ ++void lzds_dasd_get_cylinders(struct dasd *dasd, unsigned int *cylinders); ++ ++/** ++ * @brief Get the number of heads, that a cylinder of this DASD has. ++ */ ++void lzds_dasd_get_heads(struct dasd *dasd, unsigned int *heads); ++ ++/** ++ * @brief Allocate a new dasd context structure for given data set. ++ */ ++int lzds_dasd_alloc_dasdhandle(struct dasd *dasd, struct dasdhandle **dasdh); ++ ++/** ++ * @brief Free memory that was allocated for a dasdhandle. ++ */ ++void lzds_dasdhandle_free(struct dasdhandle *dasdh); ++ ++/** ++ * @brief This makes the dasd context ready for read operations. ++ */ ++int lzds_dasdhandle_open(struct dasdhandle *dasdh); ++ ++/** ++ * @brief This closes the file descriptor connected with the dasdhandle. ++ */ ++int lzds_dasdhandle_close(struct dasdhandle *dasdh); ++ ++/** ++ * @brief Read raw tracks from the dasdhandle. ++ */ ++int lzds_dasdhandle_read_tracks_to_buffer(struct dasdhandle *dasdh, ++ unsigned int starttrck, ++ unsigned int endtrck, ++ char *trackdata); ++ ++/** @} */ /* end of group libzds_functions_low */ ++ ++ ++ ++/** ++ * @defgroup libzds_functions_mid Mid level interface functions ++ * @{ ++ * @brief Functions that give access to low level structures like VTOC ++ * records. ++ * ++ * These functions give access to and rely on the meta data stored on ++ * the DASD, in particular the VTOC. ++ * These functions take the structure of the data on the ++ * device into account, so they may fail if this data is not ++ * correct (e.g. if the VTOC is broken). ++ * ++ * @todo The interface of the mid level functions is not properly structured yet. ++ * ++ */ ++ ++/** ++ * @brief Read the volume label from device. The data as stored as ++ * part of the struct dasd. ++ */ ++int lzds_dasd_read_vlabel(struct dasd *dasd); ++ ++/** ++ * @brief Get the previously read volume label data.. ++ */ ++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); ++ ++/** ++ * @brief Get the previously read raw_vtoc data. ++ */ ++int lzds_dasd_get_rawvtoc(struct dasd *dasd, struct raw_vtoc **vtoc); ++ ++/** ++ * @brief Allocate index that allows to iterate through all DSCB ++ * records stored in the raw_vtoc. ++ */ ++int lzds_raw_vtoc_alloc_dscbiterator(struct raw_vtoc *rawvtoc, ++ struct dscbiterator **it); ++/** ++ * @brief Free the iterators memory ++ */ ++void lzds_dscbiterator_free(struct dscbiterator *it); ++ ++/** ++ * @brief Get the next DSCB in the VTOC. ++ */ ++int lzds_dscbiterator_get_next_dscb(struct dscbiterator *it, ++ struct dscb **dscb); ++/** ++ * @brief Find and get a specific DSCB record by its cylinder, head ++ * and record address (cchhb_t) ++ */ ++int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p, ++ struct dscb **dscb); ++ ++/** @} */ /* end of group libzds_functions_mid */ ++ ++ ++ ++ ++ ++/** ++ * @defgroup libzds_functions_high High level interface functions ++ * @{ ++ * @brief These functions give access to the data on a DASD on a ++ * high abstraction level. ++ * ++ * These functions abstract away most of the low level details. ++ * They give access to the user data stored on the DASD using abstract ++ * concepts like 'data set' without requiring the user ++ * to do any low level analysis. ++ */ ++ ++/** ++ * @brief Search zdsroot for a specific data set. ++ */ ++int lzds_zdsroot_find_dataset(struct zdsroot *root, const char *name, ++ struct dataset **ds); ++ ++/** ++ * @brief Allocate an iterator that will allow to iterate through all ++ * datasets on the index. ++ */ ++int lzds_zdsroot_alloc_dsiterator(struct zdsroot *zdsroot, ++ struct dsiterator **it); ++/** ++ * @brief Free memory of the given data set iterator. ++ */ ++void lzds_dsiterator_free(struct dsiterator *it); ++ ++/** ++ * @brief Return the next data set the iterator points to. ++ */ ++int lzds_dsiterator_get_next_dataset(struct dsiterator *it, ++ struct dataset **ds); ++ ++/** ++ * @brief Get the 'partitioned data set' status of a data set? ++ */ ++void lzds_dataset_get_is_PDS(struct dataset *ds, int *ispds); ++ ++/** ++ * @brief Are all parts of a multi volume data set available? ++ */ ++void lzds_dataset_get_is_complete(struct dataset *ds, int *iscomplete); ++ ++/** ++ * @brief Can the data set be opened and read with this library? ++ */ ++void lzds_dataset_get_is_supported(struct dataset *ds, int *issupported); ++ ++/** ++ * @brief Get the name of a dataset as ASCII string. ++ */ ++void lzds_dataset_get_name(struct dataset *ds, char **name); ++ ++/** ++ * @brief Get the format 1 DSCB for a data set. In case of a multi volume ++ * data set it returns the DSCB of the first volume. ++ */ ++void lzds_dataset_get_format1_dscb(struct dataset *ds, format1_label_t **f1); ++ ++/** ++ * @brief Search the data set for a given member name and if a matching ++ * member is found return a struct pdsmember. ++ */ ++int lzds_dataset_get_member_by_name(struct dataset *ds, char *membername, ++ struct pdsmember **member); ++ ++/** ++ * @brief Allocate an iterator that will allow to iterate through all members ++ * on a datasets. ++ */ ++int lzds_dataset_alloc_memberiterator(struct dataset *ds, ++ struct memberiterator **it); ++ ++/** ++ * @brief Free memory of the given member iterator. ++ */ ++void lzds_memberiterator_free(struct memberiterator *it); ++ ++/** ++ * @brief Return the next data set member the iterator points to. ++ */ ++int lzds_memberiterator_get_next_member(struct memberiterator *it, ++ struct pdsmember **member); ++ ++/** ++ * @brief Allocate a new data set context structure for given data set. ++ */ ++int lzds_dataset_alloc_dshandle(struct dataset *ds, ++ unsigned int tracks_per_frame, ++ struct dshandle **dsh); ++ ++/** ++ * @brief Free the memory of the given dshandle structure. ++ */ ++void lzds_dshandle_free(struct dshandle *dsh); ++ ++/** ++ * @brief If the dsh points to a partitioned data set, this function will ++ * set which member of that PDS is read via the dsh. ++ */ ++int lzds_dshandle_set_member(struct dshandle *dsh, char *membername); ++ ++/** ++ * @brief Read out the member pointer that has been set on this dshandle. ++ */ ++void lzds_dshandle_get_member(struct dshandle *dsh, ++ struct pdsmember **member); ++ ++/** ++ * @brief Set the flag that causes the library to keep the record descriptor ++ * word (RDW) of variable records in the data stream. ++ */ ++int lzds_dshandle_set_keepRDW(struct dshandle *dsh, int keepRDW); ++ ++/** ++ * @brief Read out the current setting of the RDW flag. ++ */ ++void lzds_dshandle_get_keepRDW(struct dshandle *dsh, int *keepRDW); ++ ++/** ++ * @brief Prepares the dsh and the related devices for read operations. ++ */ ++int lzds_dshandle_open(struct dshandle *dsh); ++ ++/** ++ * @brief Matching close operation for the data set context. ++ */ ++void lzds_dshandle_close(struct dshandle *dsh); ++ ++/** ++ * @brief Read data from the data set to which the dsh points. ++ */ ++int lzds_dshandle_read(struct dshandle *dsh, char *buf, ++ size_t size, ssize_t *rcsize); ++ ++/** ++ * @brief Move buffer position of dsh to offset. ++ */ ++int lzds_dshandle_lseek(struct dshandle *dsh, long long offset, ++ long long *rcoffset); ++ ++/** ++ * @brief Get the current buffer position. ++ */ ++void lzds_dshandle_get_offset(struct dshandle *dsh, long long *offset); ++ ++/** ++ * @brief Get the errorlog. ++ */ ++void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log); ++ ++/** ++ * @brief Set an upper limit for the seek buffer. ++ */ ++int lzds_dshandle_set_seekbuffer(struct dshandle *dsh, ++ unsigned long long seek_buffer_size); ++ ++/** ++ * @brief Get the size of the data set in number of tracks (sum of all extents). ++ */ ++void lzds_dataset_get_size_in_tracks(struct dataset *ds, ++ unsigned long long *tracks); ++ ++/** ++ * @brief Get the name of a partitioned dataset member. ++ */ ++void lzds_pdsmember_get_name(struct pdsmember *member, char **name); ++ ++ ++/** ++ * @brief Extract the data set information from the rawvtoc stored in the ++ * dasd and add it to the list of data sets stored in the zdsroot. ++ */ ++int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root, ++ struct dasd *dasd); ++ ++ ++ ++/** @} */ /* end of group libzds_functions_high */ ++ ++ ++ ++/** ++ * @defgroup libzds_functions_helper Helper functions ++ * @{ ++ * ++ * @brief These functions do not fit in the hierarchy of the other ++ * libzds functions, but are usefull helpers. ++ */ ++ ++/** ++ * @brief Translates a DS1RECFM byte to a recfm format string. ++ */ ++void lzds_DS1RECFM_to_recfm(char DS1RECFM, char *buffer); ++ ++ ++ ++/** @} */ /* end of group libzds_functions_helper */ ++ ++ ++ ++ ++#endif /* LIBZDS_H */ +diff --git a/include/vtoc.h b/include/vtoc.h +index 0379b69..7867343 100644 +--- a/include/vtoc.h ++++ b/include/vtoc.h +@@ -4,7 +4,7 @@ + * + * This is a user-space copy of the kernel vtoc,h. + * +- * Copyright IBM Corp. 2002,2012 ++ * Copyright IBM Corp. 2002, 2013 + * + * History of changes (starts March 2002) + * 2002-03-12 initial +@@ -174,6 +174,16 @@ typedef struct format1_label + } __attribute__ ((packed)) format1_label_t; + + ++typedef struct format3_label ++{ ++ char DS3KEYID[4]; /* key identifier */ ++ extent_t DS3EXTNT[4]; /* first 4 extent descriptions */ ++ u_int8_t DS3FMTID; /* format identifier */ ++ extent_t DS3ADEXT[9]; /* last 9 extent description */ ++ cchhb_t DS3PTRDS; /* pointer to next format3 DSCB */ ++} __attribute__ ((packed)) format3_label_t; ++ ++ + typedef struct format4_label + { + char DS4KEYCD[44]; /* key code for VTOC labels: 44 times 0x04 */ +@@ -252,7 +262,8 @@ typedef struct format9_label + u_int8_t DS9NUMF9; /* number of F9 datasets */ + u_int8_t res1[41]; /* reserved */ + u_int8_t DS9FMTID; /* format identifier */ +- u_int8_t res2[95]; /* reserved */ ++ u_int8_t res2[90]; /* reserved */ ++ cchhb_t DS9PTRDS; /* pointer to next DSCB */ + } __attribute__ ((packed)) format9_label_t; + + char * vtoc_ebcdic_enc (char *source, char *target, int l); +diff --git a/libzds/Makefile b/libzds/Makefile +new file mode 100644 +index 0000000..09b67e9 +--- /dev/null ++++ b/libzds/Makefile +@@ -0,0 +1,22 @@ ++include ../common.mak ++ ++CPPFLAGS += -I../include ++CFLAGS += -D_FILE_OFFSET_BITS=64 ++ ++%.a: %.o ++ $(AR) rcs $@ $^ ++ ++all: libzds.a ++ ++libzds.a: libzds.o ../libutil/util_list.o ../libvtoc/vtoc.o ++ ++libzds.o: ../include/libzds.h ++ ++util_list.o: util.h ++ ++install: all ++ ++clean: ++ rm -f *.o *.a *~ core ++ ++.PHONY: all install clean +diff --git a/libzds/libzds.c b/libzds/libzds.c +new file mode 100644 +index 0000000..a4899cf +--- /dev/null ++++ b/libzds/libzds.c +@@ -0,0 +1,3774 @@ ++/** ++ * @file libzds.c ++ * This is the implementation of the internal library libzds. ++ * Please note that this library should currently only be used ++ * by programs in the s390-tools package. It is not yet meant ++ * for external use as interfaces and definitions may change ++ * without further notice. ++ * ++ * Copyright IBM Corporation 2013 ++ */ ++ ++/** ++ * @brief Define _GNU_SOURCE to enable O_DIRECT ++ */ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#include "vtoc.h" ++#include "libzds.h" ++ ++#include "util.h" ++ ++#include ++#include ++ ++ ++ ++/** @cond PRIVATE */ ++ ++/******************************************************************************/ ++/* ioctl related definitions */ ++/******************************************************************************/ ++ ++/* device size in bytes (u64 *arg) */ ++#define BLKGETSIZE64 _IOR(0x12, 114, size_t) ++ ++ ++/******************************************************************************/ ++/* libzds structure definitions */ ++/******************************************************************************/ ++ ++/** ++ * @brief Maximum size of a volume serial string (NOT including one byte for ++ * 0-termination) ++ */ ++#define MAXVOLSER 6 ++ ++ ++/* ++ * The following structures are declared in libzds.h but defined here in ++ * the .c file to keep them opaque to the user of the library. ++ */ ++ ++ ++struct errorlog { ++ struct util_list *entries; ++}; ++ ++/** ++ * @brief Size of the message buffer in errormsg ++ * ++ */ ++#define ERRORMSG 240 ++ ++/** ++ * @brief An internal structure that represents an entry in the error log. ++ */ ++struct errormsg { ++ /** @brief List head to store a list of errormsg in struct errorlog */ ++ struct util_list_node list; ++ /** @brief error code that was associated with this message*/ ++ int error; ++ /** @brief a descriptive message text */ ++ 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; ++ /** @brief Index to the vtocindex array in rawvtoc */ ++ 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; ++ /** @brief File descriptor for the block device. ++ * Should be -1 when the device not open */ ++ int fd; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; ++ ++struct pdsmember { ++ /** @brief List head that is used to store a list of members in ++ * struct dataset */ ++ struct util_list_node list; ++ ++ /** @brief Member name, converted from EBCDIC to ASCII */ ++ char name[MEMBERNAMELENGTH]; ++ /** @brief The track the member starts in, relative to the data set. ++ * ++ * @note This number is relative to the data set, with track 0 ++ * being the first track of the data set. It is independent ++ * of the DASD geometry or extent location. */ ++ unsigned short track; ++ /** @brief First record of the member starts in, relative to the ++ * start track.*/ ++ unsigned char record; ++ /** @brief Marks if pdsmember is an alias (we make no distinction ++ * between a regular member and an alias). */ ++ unsigned char is_alias; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; ++ ++ ++/** ++ * @brief An internal structure that represents part of a multi volume data set ++ * ++ * Data sets can be spread over several DASD devices (multi volume data set), ++ * and this structure represents one such part. Each data set has at least one ++ * datasetpart. ++ */ ++struct datasetpart { ++ /** @brief The dasd that this part resides on */ ++ struct dasd *dasdi; ++ /** @brief Pointer to the respective format 1 DSCB in the raw_vtoc ++ * of that dasd */ ++ format1_label_t *f1; ++ /** @brief Each part can consist of up to MAXEXTENTS (16) extents */ ++ extent_t ext[MAXEXTENTS]; ++}; ++ ++ ++struct dataset { ++ /** @brief List head that is used to store a list of data sets in ++ * struct zdsroot */ ++ struct util_list_node list; ++ ++ /** @brief Data set name, translated from EBCDIC to ASCII, 0-terminated ++ * and with any blank padding removed */ ++ char name[MAXDSNAMELENGTH]; ++ /** @brief Array of data set parts this data set consists of. ++ * ++ * We use just an regular array as the number of parts is limited. ++ * Each part has a specific position, as defined by the DS1VOLSQ ++ * value in the parts format 1 label. ++ */ ++ struct datasetpart *dsp[MAXVOLUMESPERDS]; ++ /** @brief Number of parts this data set has ++ * ++ * @note This is the number of data set parts we have already found. ++ * As long as there are still gaps in the dsp array, dspcount may be ++ * smaller than the largest index of an element in the dsp array. ++ */ ++ int dspcount; ++ /** @brief Flag that is set to 1 if we have all parts ++ * ++ * @note: In cases where a dataset consists of only one part, the ++ * the fist part should be flagged as last part as well in DS1DSIND, ++ * but this seems not to be reliable. So, as long as we only have only ++ * found one part in position 0, we may set iscomplete, even if we ++ * have no 'last part' marker found. ++ */ ++ int iscomplete; ++ /** @brief If a data set is a partitioned data set (PDS), then this ++ * contains a list of members, otherwise the list is empty */ ++ struct util_list *memberlist; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; ++ ++struct memberiterator { ++ /** @brief Data set that holds the members */ ++ struct dataset *ds; ++ /** @brief The last selected member. */ ++ struct pdsmember *memberi; ++}; ++ ++struct dsiterator { ++ /** @brief zdsroot that holds the data sets */ ++ struct zdsroot *zdsroot; ++ /** @brief The last selected data set */ ++ struct dataset *dsi; ++}; ++ ++struct dasditerator { ++ /** @brief zdsroot that holds the dasds */ ++ struct zdsroot *zdsroot; ++ /** @brief The last selected dasd */ ++ struct dasd *dasdi; ++}; ++ ++struct zdsroot { ++ /** @brief list of dasds */ ++ struct util_list *dasdlist; ++ /** @brief list of data sets */ ++ struct util_list *datasetlist; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; ++ ++/** ++ * @brief Internal structure to keep track of offsets in the data set ++ */ ++struct seekelement { ++ /** @brief Data set part this element refers to */ ++ unsigned char dsp_no; ++ /** @brief The extent on that part/dasd */ ++ unsigned char ext_seq_no; ++ /** @brief The starting track on that part/dasd */ ++ unsigned int bufstarttrk; ++ /** @brief The absolute offset in the data set */ ++ long long databufoffset; ++}; ++ ++/** ++ * @brief Default value for the tracks value in dshandle ++ */ ++#define TRACK_BUFFER_DEFAULT 128 ++ ++struct dshandle { ++ /** @brief Data set this context relates to */ ++ struct dataset *ds; ++ /** @brief Pointer to member, only applicable to PDS */ ++ struct pdsmember *member; ++ /** @brief One dasdhandle per data set part ++ * ++ * The dshandle functions do not read directly from the devices, ++ * instead they use the dasdhandle interfacesw. ++ */ ++ struct dasdhandle *dasdhandle[MAXVOLUMESPERDS]; ++ ++ /** @brief A multiplier that is used to determine the various ++ buffer sizes. Number of tracks in one track frame. */ ++ unsigned int tracks_per_frame; ++ ++ /** @brief Flag: While interpreting the data, keep the record ++ * descriptor words in the data stream */ ++ int keepRDW; ++ /** @brief Flag that is set between open and close */ ++ int is_open; ++ /** @brief This flag is set when during interpretation of the track ++ * buffer the end of the data is found */ ++ int eof_reached; ++ ++ ++ /* The following values describe our current position within the data ++ * set */ ++ ++ /** @brief Index number of the current data set part */ ++ int dsp_no; ++ /** @brief The sequence number of the current extent in the current ++ * data set part */ ++ int ext_seq_no; ++ /** @brief Start buffer interpretation at this record. ++ * ++ * Data set members may start in the middle of a track. So we need ++ * to know with which record to start. ++ */ ++ unsigned char startrecord; ++ /** @brief The first track of the extent that dsp_no and ext_seq_no ++ * point to */ ++ unsigned int extstarttrk; ++ /** @brief The last track of the extent that dsp_no and ext_seq_no ++ * point to */ ++ unsigned int extendtrk; ++ /** @brief Start of the area that is currently in the rawbuffer */ ++ unsigned int bufstarttrk; ++ /** @brief End of the area that is currently in the rawbuffer */ ++ unsigned int bufendtrk; ++ /** @brief Running number of the current track frame */ ++ long long frameno; ++ ++ /** @brief Buffer for the raw track images */ ++ char *rawbuffer; ++ /** @brief Buffer for the extracted user data */ ++ char *databuffer; ++ /** @brief Size of the rawbuffer */ ++ long long rawbufmax; ++ /** @brief Size of the databuffer */ ++ long long databufmax; ++ /** @brief Size of the currently used part of the rawbuffer */ ++ long long rawbufsize; ++ /** @brief Size of the currently used part of the databuffer */ ++ long long databufsize; ++ /** @brief Current position of the databuffer relative to the begin ++ * of the data set */ ++ long long databufoffset; ++ /** @brief Current position in the databuffer */ ++ long long bufpos; ++ ++ ++ /** @brief Buffer for seek data points */ ++ struct seekelement *seekbuf; ++ /** @brief Total number of elements in seekbuf */ ++ unsigned long long seek_count; ++ /** @brief Number of used elements in seekbuf */ ++ unsigned long long seek_current; ++ /** @brief Modulo that determines which track frame is stored in the ++ * seek buffer ++ * ++ * Example: If skip is 2, then every 2'nd frame is stored. ++ */ ++ unsigned long long skip; ++ /** @brief Detailed error messages in case of a problem */ ++ struct errorlog *log; ++}; ++ ++#define min(x, y) ((x) > (y) ? (y) : (x)) ++ ++/** @endcond */ ++ ++ ++/******************************************************************************/ ++/* BASIC level functions */ ++/******************************************************************************/ ++ ++static void dasd_free(struct dasd *dasd); ++static void dataset_free_memberlist(struct dataset *ds); ++static void errorlog_free(struct errorlog *log); ++static void errorlog_clear(struct errorlog *log); ++static int errorlog_add_message(struct errorlog **log, ++ struct errorlog *oldlog, ++ int error_code, ++ const char *message_format, ++ ...) __attribute__ ((format (printf, 4, 5))); ++ ++ ++ ++ ++ ++/** ++ * Since the zdsroot is the root for all the other data structures, ++ * this should be one of the first functions to call. ++ * @param[out] root Reference to a pointer variable in which the newly ++ * allocated structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_zdsroot_alloc(struct zdsroot **root) ++{ ++ struct zdsroot *tmproot; ++ ++ *root = NULL; ++ tmproot = malloc(sizeof(*tmproot)); ++ if (!tmproot) ++ return ENOMEM; ++ memset(tmproot, 0, sizeof(*tmproot)); ++ ++ tmproot->dasdlist = util_list_new(struct dasd, list); ++ if (!tmproot->dasdlist) { ++ free(tmproot); ++ return ENOMEM; ++ } ++ ++ tmproot->datasetlist = util_list_new(struct dataset, list); ++ if (!tmproot->dasdlist) { ++ util_list_free(tmproot->dasdlist); ++ free(tmproot); ++ return ENOMEM; ++ } ++ ++ *root = tmproot; ++ ++ 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_zdsroot_free(struct zdsroot *root) ++{ ++ struct dasd *dasd, *nextdasd; ++ struct dataset *ds, *nextds; ++ int i; ++ ++ if (!root) ++ return; ++ ++ util_list_iterate_safe(root->dasdlist, dasd, nextdasd) { ++ util_list_remove(root->dasdlist, dasd); ++ 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); ++ } ++ util_list_free(root->datasetlist); ++ errorlog_free(root->log); ++ free(root); ++} ++ ++/** ++ * @brief Subroutine of lzds_zdsroot_add_device ++ * ++ * This function determines some basic DASD geometry information and stores ++ * it in the struct dasd for later use. ++ * ++ * @param[in] dasd Reference to the dasd to work on. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EIO Some error prevented us from gaining this information ++ * ++ */ ++static int dasd_read_geometry(struct dasd *dasd) ++{ ++ int fd; ++ unsigned long long size_in_bytes; ++ ++ errorlog_clear(dasd->log); ++ fd = open(dasd->device, O_RDONLY); ++ if (fd < 0) ++ return errorlog_add_message( ++ &dasd->log, NULL, EIO, ++ "read geometry: could not open device %s\n", ++ dasd->device); ++ ++ if (ioctl(fd, BLKGETSIZE64, &size_in_bytes) != 0) ++ return errorlog_add_message( ++ &dasd->log, NULL, EIO, ++ "read geometry: could not get size from device %s\n", ++ dasd->device); ++ ++ /* label_block and heads are simply hard coded with the correct values ++ * for ECKD DASDs. This makes us independent from any DASD specific ++ * ioctls like BIODASDINFO and allows us to work on DASD images via ++ * loopback device. ++ */ ++ dasd->label_block = 2; ++ dasd->heads = 15; ++ dasd->cylinders = (size_in_bytes / (dasd->heads * RAWTRACKSIZE)); ++ close(fd); ++ return 0; ++} ++ ++ ++/** ++ * @brief Subroutine of lzds_zdsroot_add_device ++ * ++ * This function goes through the list of dasds in root and verifies that ++ * the a dasd with the given device name is not yet present. ++ * @param[in] root Reference to the zdsroot structure the new struct dasd ++ * is to be added to. ++ * @param[in] devnode String that holds the name of the device node, ++ * e.g. "/dev/dasdb". ++ * @return true if matching dasd has been found, false if not ++ */ ++static int zdsroot_is_duplicate_device(struct zdsroot *root, ++ const char *devnode) ++{ ++ struct dasd *dasd; ++ ++ dasd = NULL; ++ lzds_zdsroot_get_dasd_by_node_name(root, devnode, &dasd); ++ return !(dasd == NULL); ++} ++ ++/** ++ * This function creates a new struct dasd and adds it to the root. ++ * It can later be traversed using the dasditerator functions. ++ * ++ * @param[in] root Reference to the zdsroot structure the new struct dasd ++ * is to be added to. ++ * @param[in] devnode String that holds the name of the device node, ++ * e.g. "/dev/dasdb". ++ * @param[out] dasd Reference to a pointer variable in which the newly ++ * allocated structure will be returned. ++ * This pointer is returned for the convenience of the user, ++ * to be used with follow on calls, e.g to lzds_dasd_read_vlabel. ++ * ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ * - ENOTTY The used ioctl is not supported by the device (i.e. the ++ * device is not a DASD.) ++ * - EIO Some other error prevented us from gaining this information ++ * ++ * @note It is not guaranteed that ENOTTY is returned when the device is ++ * not a DASD. It depends on the device whether ENOTTY or EIO is returned. ++ */ ++int lzds_zdsroot_add_device(struct zdsroot *root, const char *devnode, ++ struct dasd **dasd) ++{ ++ struct dasd *dasdtmp; ++ int rc; ++ ++ errorlog_clear(root->log); ++ if (zdsroot_is_duplicate_device(root, devnode)) { ++ return errorlog_add_message( ++ &root->log, NULL, EINVAL, ++ "add device: duplicate device %s\n", ++ devnode); ++ } ++ dasdtmp = malloc(sizeof(*dasdtmp)); ++ if (!dasdtmp) ++ return ENOMEM; ++ memset(dasdtmp, 0, sizeof(*dasdtmp)); ++ dasdtmp->device = strdup(devnode); ++ dasdtmp->inusefd = open(dasdtmp->device, O_RDONLY); ++ if (dasdtmp->inusefd < 0) { ++ errorlog_add_message( ++ &root->log, dasdtmp->log, EIO, ++ "add device: could open device %s\n", ++ dasdtmp->device); ++ dasd_free(dasdtmp); ++ return EIO; ++ } ++ rc = dasd_read_geometry(dasdtmp); ++ if (rc) { ++ errorlog_add_message( ++ &root->log, dasdtmp->log, EIO, ++ "add device: could not read device data from %s\n", ++ dasdtmp->device); ++ close(dasdtmp->inusefd); ++ dasd_free(dasdtmp); ++ return EIO; ++ } ++ util_list_add_tail(root->dasdlist, dasdtmp); ++ if (dasd) ++ *dasd = dasdtmp; ++ return 0; ++} ++ ++/** ++ * @param[in] dasd A dasd on which an error occurred. ++ * @param[out] log Reference to a variable in which the errorlog ++ * is returned. ++ */ ++void lzds_dasd_get_errorlog(struct dasd *dasd, struct errorlog **log) ++{ ++ *log = dasd->log; ++} ++ ++/** ++ * @brief Subroutine of lzds_zdsroot_free. Frees the struct dasd and everything ++ * that belogns to it. ++ * ++ * @param[in] dasd Pointer to the struct dasd that is to be freed. ++ */ ++static void dasd_free(struct dasd *dasd) ++{ ++ free(dasd->device); ++ free(dasd->vlabel); ++ if (dasd->rawvtoc) { ++ free(dasd->rawvtoc->rawdata); ++ free(dasd->rawvtoc->vtocindex); ++ errorlog_free(dasd->rawvtoc->log); ++ free(dasd->rawvtoc); ++ } ++ errorlog_free(dasd->log); ++ close(dasd->inusefd); ++ free(dasd); ++}; ++ ++/** ++ * @param[in] zdsroot Reference to struct zdsroot that the iterator will be ++ * bound to. The iterator will traverse the dasds stored ++ * in this zdsroot. ++ * @param[out] it Reference to a pointer variable in which the newly allocated ++ * structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_zdsroot_alloc_dasditerator(struct zdsroot *zdsroot, ++ struct dasditerator **it) ++{ ++ *it = malloc(sizeof(struct dasditerator)); ++ if (*it) { ++ (*it)->dasdi = NULL; ++ (*it)->zdsroot = zdsroot; ++ return 0; ++ } ++ return ENOMEM; ++} ++ ++/** ++ * @param[in] it Pointer to the struct dasditerator that is to be freed. ++ */ ++void lzds_dasditerator_free(struct dasditerator *it) ++{ ++ free(it); ++} ++ ++/** ++ * @param[out] it Reference to the struct dasditerator we use to traverse the ++ * dasd list. ++ * @param[out] dasd Reference to a pointer variable in which the next dasd in ++ * the sequence will be returned. If there is no next DASD, ++ * this variable will be set to NULL. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPERM The end of the list has been reached. There is no further dasd. ++ */ ++int lzds_dasditerator_get_next_dasd(struct dasditerator *it, struct dasd **dasd) ++{ ++ struct dasd *dasdtmp; ++ ++ if (!it->dasdi) ++ dasdtmp = util_list_start(it->zdsroot->dasdlist); ++ else ++ dasdtmp = util_list_next(it->zdsroot->dasdlist, it->dasdi); ++ *dasd = dasdtmp; ++ if (!dasdtmp) ++ return EPERM; ++ it->dasdi = dasdtmp; ++ return 0; ++} ++ ++/** ++ * @param[in] dasd The struct dasd we want to know the device of. ++ * @param[out] device Reference to a pointer variable in which the device ++ * string will be returned. This string holds the device ++ * name as it was given to lzds_zdsroot_add_device. ++ */ ++void lzds_dasd_get_device(struct dasd *dasd, char **device) ++{ ++ *device = dasd->device; ++} ++ ++/** ++ * @param[in] root Reference to the zdsroot that holds the dasd. ++ * @param[in] device Pointer to a character string that holds the device node ++ * name that we are looking for. It must be the same name as ++ * previously given to lzds_zdsroot_add_device ++ * @param[out] dasd Reference to a pointer variable in which the found struct ++ * dasd will be returned. If no dasd was found, ++ * this will be set to NULL ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structures due to lack of memory. ++ * - ENODEV No matching struct dasd was found. ++ */ ++int lzds_zdsroot_get_dasd_by_node_name(struct zdsroot *root, const char *device, ++ struct dasd **dasd) ++{ ++ struct dasditerator *dasdit; ++ int rc; ++ struct dasd *tempdasd; ++ char *dasddev; ++ ++ errorlog_clear(root->log); ++ rc = lzds_zdsroot_alloc_dasditerator(root, &dasdit); ++ if (rc) ++ return ENOMEM; ++ rc = ENODEV; ++ *dasd = NULL; ++ while (!lzds_dasditerator_get_next_dasd(dasdit, &tempdasd)) { ++ lzds_dasd_get_device(tempdasd, &dasddev); ++ if (!strcmp(device, dasddev)) { ++ rc = 0; ++ *dasd = tempdasd; ++ break; ++ } ++ } ++ lzds_dasditerator_free(dasdit); ++ return rc; ++}; ++ ++/** ++ * @param[in] root A zdsroot on which an error occurred. ++ * @param[out] log Reference to a variable in which the errorlog ++ * is returned. ++ */ ++void lzds_zdsroot_get_errorlog(struct zdsroot *root, struct errorlog **log) ++{ ++ *log = root->log; ++} ++ ++ ++/** ++ * @brief free storage for a single error message ++ * ++ * @param[in] msg The message to be freed ++ */ ++static void errormsg_free(struct errormsg *msg) ++{ ++ free(msg); ++} ++ ++/** ++ * @brief allocate storage for a single error message ++ * ++ * @param[out] msg Reference to a pointer variable in which the newly allocated ++ * structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++static int errormsg_alloc(struct errormsg **msg) ++{ ++ struct errormsg *tmpmsg; ++ ++ *msg = NULL; ++ tmpmsg = malloc(sizeof(*tmpmsg)); ++ if (!tmpmsg) ++ return ENOMEM; ++ memset(tmpmsg, 0, sizeof(*tmpmsg)); ++ *msg = tmpmsg; ++ return 0; ++} ++ ++/** ++ * @brief remove and free all messages from a given errolog ++ * ++ * After this operation new messages can be added to the log. ++ * ++ * @param[in] log The message log to be cleared. This may be NULL. ++ */ ++static void errorlog_clear(struct errorlog *log) ++{ ++ struct errormsg *msg, *nextmsg; ++ ++ if (!log) ++ return; ++ util_list_iterate_safe(log->entries, msg, nextmsg) { ++ util_list_remove(log->entries, msg); ++ errormsg_free(msg); ++ } ++} ++ ++/** ++ * @brief free storage for an error log, including all messages ++ * ++ * @param[in] log The error log to be freed. This may be NULL. ++ */ ++static void errorlog_free(struct errorlog *log) ++{ ++ if (!log) ++ return; ++ errorlog_clear(log); ++ util_list_free(log->entries); ++ free(log); ++} ++ ++/** ++ * @brief allocate storage for an error log ++ * ++ * @param[out] log Reference to a pointer variable in which the newly allocated ++ * structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++static int errorlog_alloc(struct errorlog **log) ++{ ++ struct errorlog *tmplog; ++ ++ *log = NULL; ++ tmplog = malloc(sizeof(*tmplog)); ++ if (!tmplog) ++ return ENOMEM; ++ memset(tmplog, 0, sizeof(*tmplog)); ++ tmplog->entries = util_list_new(struct errormsg, list); ++ if (!tmplog->entries) { ++ free(tmplog); ++ return ENOMEM; ++ } ++ *log = tmplog; ++ return 0; ++} ++ ++/** ++ * @brief add a new message to the front of a log. ++ * ++ * @param[out] log A reference to a errorlog pointer variable. If a log already ++ * exists, old messages are cleared, otherwise a new log will ++ * be created. ++ * @param[in] oldlog A log that already contains messages, usually from a call ++ * to a subordinate function. This may be the same errorlog as ++ * referenced by log, in which case the existing messages ++ * are retained. This may also be NULL. ++ * @param[in] error_code The error code that will be stored in the new errormsg. ++ * This is also the return value. ++ * @param[in] message_format A format string for the message string ++ * (see vsnprintf man page). ++ * @param[in] ... A variable number of further parameters. ++ * Must match the message_format string. ++ */ ++static int errorlog_add_message(struct errorlog **log, ++ struct errorlog *oldlog, ++ int error_code, ++ const char *message_format, ++ ...) ++{ ++ struct errormsg *msg, *nextmsg; ++ struct errorlog *tmplog; ++ va_list ap; ++ int rc; ++ ++ if (!log) ++ return error_code; ++ if (log && !*log) { ++ errorlog_alloc(&tmplog); ++ if (!tmplog) ++ return error_code; ++ *log = tmplog; ++ } else { ++ tmplog = *log; ++ } ++ ++ if (tmplog != oldlog) { ++ errorlog_clear(tmplog); ++ if (oldlog) { ++ util_list_iterate_safe(oldlog->entries, msg, nextmsg) { ++ util_list_remove(oldlog->entries, msg); ++ util_list_add_tail(tmplog->entries, msg); ++ } ++ } ++ } ++ ++ if (!message_format) ++ return error_code; ++ ++ rc = errormsg_alloc(&msg); ++ if (rc) ++ return error_code; ++ ++ va_start(ap, message_format); ++ vsnprintf(msg->text, ERRORMSG - 1, message_format, ap); ++ va_end(ap); ++ msg->error = error_code; ++ util_list_add_head(tmplog->entries, msg); ++ ++ return error_code; ++} ++ ++/** ++ * This is pretty a very simple implementation that just goes through ++ * the list of messages in the log and for each message it prints ++ * "rc : " ++ * ++ * @param[in] log A log that contains messages. ++ * @param[in] stream The stream that these messages will be printed to. ++ */ ++int lzds_errorlog_fprint(struct errorlog *log, FILE *stream) ++{ ++ struct errormsg *msg; ++ int rc; ++ ++ if (!log) ++ return 0; ++ util_list_iterate(log->entries, msg) { ++ rc = fprintf(stream, "rc %d: %s", msg->error, msg->text); ++ if (rc < 0) ++ return -rc; ++ } ++ return 0; ++} ++ ++ ++ ++/******************************************************************************/ ++/* LOW level functions */ ++/******************************************************************************/ ++ ++ ++/** ++ * @param[in] dasd The DASD to whose geometry we refer to. ++ * @param[in] p Cylinder and head address ++ * @param[out] track The sequential track number for the given ++ * cylinder and head address. ++ */ ++void lzds_dasd_cchh2trk(struct dasd *dasd, cchh_t *p, unsigned int *track) ++{ ++ *track = vtoc_get_cyl_from_cchh(p) * dasd->heads + ++ vtoc_get_head_from_cchh(p); ++} ++ ++/** ++ * @param[in] dasd The DASD to whose geometry we refer to. ++ * @param[out] cylinders The number of cylinders that DASD has ++ */ ++void lzds_dasd_get_cylinders(struct dasd *dasd, unsigned int *cylinders) ++{ ++ *cylinders = dasd->cylinders; ++} ++ ++/** ++ * @param[in] dasd The DASD to whose geometry we refer to ++ * @param[out] heads The number of heads that DASD has ++ */ ++void lzds_dasd_get_heads(struct dasd *dasd, unsigned int *heads) ++{ ++ *heads = dasd->heads; ++} ++ ++/** ++ * @param[in] dasd Reference to struct dasd that represents ++ * the DASD that we want to read from. ++ * @param[out] dasdh Reference to a pointer variable in which the newly ++ * allocated structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_dasd_alloc_dasdhandle(struct dasd *dasd, struct dasdhandle **dasdh) ++{ ++ struct dasdhandle *dasdhtmp; ++ ++ dasdhtmp = malloc(sizeof(*dasdhtmp)); ++ if (!dasdhtmp) ++ return ENOMEM; ++ memset(dasdhtmp, 0, sizeof(*dasdhtmp)); ++ dasdhtmp->fd = -1; ++ dasdhtmp->dasd = dasd; ++ *dasdh = dasdhtmp; ++ return 0; ++} ++ ++/** ++ * @param[in] dasdh Pointer to the struct dasdhandle that is to be freed. ++ */ ++void lzds_dasdhandle_free(struct dasdhandle *dasdh) ++{ ++ if (!dasdh) ++ return; ++ /* we close the file descriptor in case it wasn't done properly */ ++ lzds_dasdhandle_close(dasdh); ++ errorlog_free(dasdh->log); ++ free(dasdh); ++} ++ ++/** ++ * @param[in] dasdh The dasd handle for the dasd that is to be opened. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EIO Could not open underlying device. ++ */ ++int lzds_dasdhandle_open(struct dasdhandle *dasdh) ++{ ++ errorlog_clear(dasdh->log); ++ dasdh->fd = open(dasdh->dasd->device, O_RDONLY | O_DIRECT); ++ if (dasdh->fd < 0) { ++ dasdh->fd = -1; ++ return errorlog_add_message( ++ &dasdh->log, NULL, EIO, ++ "dasdhandle: could not open %s, errno %d\n", ++ dasdh->dasd->device, errno); ++ } ++ return 0; ++} ++ ++/** ++ * @param[in] dasdh The dasdhandle that has to be closed ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EIO Error when closing underlying dasd device. ++ */ ++int lzds_dasdhandle_close(struct dasdhandle *dasdh) ++{ ++ int rc; ++ errorlog_clear(dasdh->log); ++ rc = 0; ++ if (dasdh->fd >= 0) ++ rc = close(dasdh->fd); ++ dasdh->fd = -1; ++ if (rc) ++ return errorlog_add_message( ++ &dasdh->log, NULL, EIO, ++ "dasdhandle: could not close %s\n", ++ dasdh->dasd->device); ++ return 0; ++} ++ ++/** ++ * @param[in] dasdh The dasdhandle we are reading from ++ * @param[in] starttrck First track to read ++ * @param[in] endtrck Last track to read ++ * @param[out] trackdata Target buffer we read into, must have at least the ++ * size (endtrk - starttrk + 1) * RAWTRACKSIZE ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL starttrck or endtrck are not within the boundaries of the ++ * underlying DASD device. ++ * - EPROTO Could not read a full track image ++ * - EIO Other I/O error ++ */ ++int lzds_dasdhandle_read_tracks_to_buffer(struct dasdhandle *dasdh, ++ unsigned int starttrck, ++ unsigned int endtrck, ++ char *trackdata) ++{ ++ off_t trckseek; ++ ssize_t residual; ++ off_t rc; ++ ssize_t count; ++ ++ unsigned int cylinders; ++ unsigned int heads; ++ ++ errorlog_clear(dasdh->log); ++ /* verify that endtrck is not beyond the end of the dasd */ ++ lzds_dasd_get_cylinders(dasdh->dasd, &cylinders); ++ lzds_dasd_get_heads(dasdh->dasd, &heads); ++ if (starttrck > endtrck || endtrck >= cylinders * heads) ++ return errorlog_add_message( ++ &dasdh->log, NULL, EINVAL, ++ "dasdhandle read tracks: start %u, end %u is" ++ " out of bounds for device %s\n", ++ starttrck, endtrck, dasdh->dasd->device); ++ /* ++ * Compute seek address of the first track and number of tracks ++ * to be read. Please note that geo.sectors does not match our raw ++ * track size of 16*4KB, so we use the RAWTRACKSIZE explicitly ++ */ ++ trckseek = (off_t)starttrck * RAWTRACKSIZE; ++ /* residual is the number of bytes we still have to read */ ++ residual = (off_t)(endtrck - starttrck + 1) * RAWTRACKSIZE; ++ rc = lseek(dasdh->fd, trckseek, SEEK_SET); ++ if (rc < 0) ++ return errorlog_add_message( ++ &dasdh->log, NULL, EINVAL, ++ "dasdhandle read tracks: seek to %llu, failed" ++ " for device %s\n", ++ (unsigned long long)trckseek, dasdh->dasd->device); ++ ++ while (residual) { ++ count = read(dasdh->fd, trackdata, residual); ++ if (count < 0) ++ return errorlog_add_message( ++ &dasdh->log, NULL, EIO, ++ "dasdhandle read tracks: read failed" ++ " for device %s, start %u, end %u\n", ++ dasdh->dasd->device, starttrck, endtrck); ++ if (count % RAWTRACKSIZE) /* No full track read */ ++ return errorlog_add_message( ++ &dasdh->log, NULL, EPROTO, ++ "dasdhandle read tracks: read returned " ++ "unaligned data for device %s," ++ "start %u, end %u\n", ++ dasdh->dasd->device, starttrck, endtrck); ++ residual -= count; ++ trackdata += count; ++ } ++ return 0; ++} ++ ++ ++/******************************************************************************/ ++/* MID level functions */ ++/******************************************************************************/ ++/** ++ * @brief Helper function that iterates through the records in a track buffer. ++ * ++ * @param[in] buffer Address of the track buffer ++ * @param[in] size Size of the buffer ++ * @param[in,out] record Pointer that has the current record pointer as input ++ * and gets a pointer to the next record as output. ++ * If it the current record pointer is null, then the ++ * pointer to the first record is returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOENT If we have reached the end of the buffer and there are no ++ * further records ++ */ ++static int buffer_get_next_record(char *buffer, size_t size, char **record) ++{ ++ char *data, *next_record; ++ unsigned long offset; ++ unsigned int record_size; ++ struct eckd_count *ecount; ++ ++ /* If *record contains no record yet, then we return the first record */ ++ if (!*record) { ++ *record = buffer; ++ return 0; ++ } ++ data = *record; ++ ecount = (struct eckd_count *)data; ++ record_size = sizeof(*ecount) + ecount->kl + ecount->dl; ++ data += record_size; ++ next_record = NULL; ++ while (!next_record) { ++ /* check if we have reached the end of the buffer */ ++ if (data >= buffer + size) { ++ *record = NULL; ++ return ENOENT; ++ } ++ /* If the 'next' record is the pseudo record, then we have ++ * reached the end of data in this track and we have to jump ++ * to the start of the next track to find the next record. ++ */ ++ if ((*(unsigned long long *)data) == ENDTOKEN) { ++ offset = (unsigned long)data - (unsigned long)buffer; ++ offset &= ~(RAWTRACKSIZE - 1); ++ offset += RAWTRACKSIZE; ++ data = buffer + offset; ++ continue; ++ } ++ next_record = data; ++ } ++ *record = next_record; ++ return 0; ++} ++ ++/** ++ * @brief Helper function that does the whole open/read/close cycle in one go. ++ * ++ * @param[in] dasd Pointer to struct dasd that represents ++ * the DASD that we want to read from. ++ * @param[in] starttrck First track to read ++ * @param[in] endtrck Last track to read ++ * @param[out] trackdata Target buffer we read into, must have at least the ++ * size (endtrk - starttrk + 1) * RAWTRACKSIZE ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - EINVAL starttrck or endtrck are not within the boundaries of the ++ * underlying DASD device. ++ * - EPROTO Could not read a full track image ++ * - EIO Other I/O error ++ */ ++static int dasd_read_tracks(struct dasd *dasd, ++ unsigned int starttrck, ++ unsigned int endtrck, ++ char *trackdata) ++{ ++ struct dasdhandle *dasdh; ++ int rc, rc2; ++ ++ rc = lzds_dasd_alloc_dasdhandle(dasd, &dasdh); ++ if (rc) ++ return errorlog_add_message( ++ &dasd->log, dasd->log, rc, ++ "dasd read tracks: could not allocate dasdhandle\n"); ++ ++ rc = lzds_dasdhandle_open(dasdh); ++ if (rc) { ++ errorlog_add_message( ++ &dasd->log, dasdh->log, rc, ++ "dasd read tracks: could not open dasdhandle\n"); ++ lzds_dasdhandle_free(dasdh); ++ return rc; ++ } ++ rc = lzds_dasdhandle_read_tracks_to_buffer(dasdh, starttrck, ++ endtrck, trackdata); ++ if (rc) ++ errorlog_add_message( ++ &dasd->log, dasdh->log, rc, ++ "dasd read tracks: read error\n"); ++ rc2 = lzds_dasdhandle_close(dasdh); ++ /* report close error only if we had no read error */ ++ if (rc2 && !rc) { ++ errorlog_add_message( ++ &dasd->log, dasdh->log, rc, ++ "dasd read tracks: could not close dasdhandle\n"); ++ rc = rc2; ++ } ++ lzds_dasdhandle_free(dasdh); ++ return rc; ++} ++ ++/** ++ * @brief Helper function that reads a volume label from a DASD. ++ * ++ * @param[in] dasd Pointer to struct dasd that represents ++ * the DASD that we want to read from. ++ * @param[out] vlabel Buffer to read the label into. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - EIO Other I/O error ++ */ ++static int dasd_read_vlabel_to_buffer(struct dasd *dasd, ++ struct volume_label *vlabel) ++{ ++ int rc; ++ unsigned int i; ++ char *trackdata, *record; ++ struct volume_label *label; ++ struct eckd_count *ecount; ++ unsigned long labelend, trackend; ++ size_t label_size; ++ ++ trackdata = memalign(4096, RAWTRACKSIZE); /* page align for O_DIRECT */ ++ if (!trackdata) ++ return ENOMEM; ++ ++ rc = dasd_read_tracks(dasd, 0, 0, trackdata); ++ if (rc) { ++ free(trackdata); ++ return errorlog_add_message( ++ &dasd->log, dasd->log, EIO, ++ "read vlabel: could not read track 0\n"); ++ } ++ /* fist step, find label record */ ++ record = NULL; ++ label = NULL; ++ ecount = NULL; ++ i = 0; ++ while (!buffer_get_next_record(trackdata, RAWTRACKSIZE, &record)) { ++ if (i == (dasd->label_block + 1)) { ++ ecount = (struct eckd_count *)record; ++ label = (struct volume_label *)(ecount + 1); ++ break; ++ } ++ ++i; ++ } ++ if (!ecount || !label) { ++ free(trackdata); ++ return errorlog_add_message( ++ &dasd->log, dasd->log, EPROTO, ++ "read vlabel: could not find label record\n"); ++ } ++ /* verify record layout */ ++ memset(vlabel, 0, sizeof(*vlabel)); ++ labelend = (unsigned long)label + ecount->kl + ecount->dl; ++ trackend = (unsigned long)trackdata + RAWTRACKSIZE; ++ if ((ecount->kl + ecount->dl == 84) && (labelend <= trackend)) { ++ /* VOL1 label */ ++ memcpy(vlabel, label, ecount->kl + ecount->dl); ++ } else if ((ecount->kl == 0) && (labelend <= trackend)) { ++ /* LNX1 / CMS1 label */ ++ label_size = min(ecount->dl, sizeof(*vlabel) - 4); ++ memcpy(&vlabel->vollbl, label, label_size); ++ } else { ++ free(trackdata); ++ return errorlog_add_message( ++ &dasd->log, dasd->log, EPROTO, ++ "read vlabel: record layout does not match VOL1" ++ " label\n"); ++ } ++ free(trackdata); ++ return 0; ++} ++ ++/** ++ * @param[in] dasd Pointer to struct dasd that represents ++ * the DASD that we want to read from. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - EIO Other I/O error ++ */ ++int lzds_dasd_read_vlabel(struct dasd *dasd) ++{ ++ struct volume_label *vlabel; ++ int rc; ++ ++ errorlog_clear(dasd->log); ++ free(dasd->vlabel); ++ dasd->vlabel = NULL; ++ vlabel = malloc(sizeof(*vlabel)); ++ if (!vlabel) ++ return ENOMEM; ++ rc = dasd_read_vlabel_to_buffer(dasd, vlabel); ++ if (rc) ++ free(vlabel); ++ else ++ dasd->vlabel = vlabel; ++ return rc; ++} ++ ++/** ++ * @param[in] dasd Reference to struct dasd that we want to get the label ++ * from. ++ * @param[out] vlabel Reference to a pointer variable in which the struct ++ * volume_label will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL The volume lable has not yet been read from the device. ++ */ ++int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel) ++{ ++ *vlabel = dasd->vlabel; ++ if (*vlabel) ++ return 0; ++ else ++ return EINVAL; ++} ++ ++/** ++ * @param[in] rawvtoc Reference to struct raw_vtoc that the iterator will be ++ * bound to. The iterator will traverse the DSCBs stored ++ * in this raw_vtoc. ++ * @param[out] it Reference to a pointer variable in which the newly allocated ++ * structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_raw_vtoc_alloc_dscbiterator(struct raw_vtoc *rawvtoc, ++ struct dscbiterator **it) ++{ ++ *it = malloc(sizeof(**it)); ++ if (*it) { ++ (*it)->i = rawvtoc->vtocrecno - 1; ++ (*it)->rawvtoc = rawvtoc; ++ return 0; ++ } ++ return ENOMEM; ++} ++ ++/** ++ * @param[in] it Pointer to the struct dscbiterator that is to be freed. ++ */ ++void lzds_dscbiterator_free(struct dscbiterator *it) ++{ ++ free(it); ++} ++ ++/** ++ * @param[out] it Reference to the struct dscb iterator we use to traverse ++ * the VTOC. ++ * @param[out] dscb Reference to a pointer variable in which the next dscb in ++ * the sequence will be returned. If there is no next dscb, ++ * this variable will be set to NULL. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPERM There is no further DSCB in the VTOC. ++ */ ++int lzds_dscbiterator_get_next_dscb(struct dscbiterator *it, struct dscb **dscb) ++{ ++ struct eckd_count *ecount; ++ unsigned int i; ++ ++ i = it->i + 1; ++ while (i < it->rawvtoc->vtocindexcount) { ++ ecount = (struct eckd_count *)(it->rawvtoc->vtocindex[i]); ++ if (ecount && (ecount->kl == 44) && (ecount->dl == 96)) ++ break; ++ else ++ ++i; ++ } ++ if (i < it->rawvtoc->vtocindexcount) { ++ it->i = i; ++ *dscb = (struct dscb *)(it->rawvtoc->vtocindex[it->i] ++ + sizeof(*ecount)); ++ return 0; ++ } else { ++ *dscb = NULL; ++ return EPERM; ++ } ++} ++ ++/** ++ * @brief Subroutine of lzds_raw_vtoc_get_dscb_from_cchhb ++ * ++ * This function takes a cylinder, head, block address as it can be ++ * found in DSCBs and returns an index to the matching entry in the ++ * raw_vtoc vtocindex. ++ * ++ * The cchhb2blk function of the libvtoc does not work for raw devices ++ * as the 'sectors per track' value in the geo structure has no meaning ++ * for a raw DASD. We need to take this value from the context, ++ * e.g. from the format 4 label of the VTOC. ++ * Since this computation is very specialized, we can go all the way and ++ * just compute the index to the vtoc array. ++ * ++ * @param[in] rv The raw_vtoc we refer to. ++ * @param[in] p The cylinder, head, block address structure. ++ * @return index to the vtocindex array ++ */ ++static long long vtocindex_from_cchhb(struct raw_vtoc *rv, cchhb_t *p) ++{ ++ long long recno; ++ ++ recno = (long long) vtoc_get_cyl_from_cchhb(p) * ++ rv->dasd->heads * rv->vtoc_rec_per_track + ++ vtoc_get_head_from_cchhb(p) * rv->vtoc_rec_per_track + ++ p->b; ++ return recno - (rv->vtoctrackoffset * rv->vtoc_rec_per_track); ++} ++ ++/** ++ * @note A cchhb address within a VTOC dscb is often set to zero to ++ * indicate that this entry does not point anywhere. For example this ++ * is the case at the end of a format 3 dscb chain. This special case ++ * is handled by setting the dscb pointer to NULL and having a return ++ * value of 0 (no error). ++ * ++ * @param[in] rv The raw_vtoc we refer to. ++ * @param[in] p The cylinder, head, block address of the DSCB. ++ * @param[out] dscb Reference to a pointer variable in which a pointer to ++ * the respective dscb in the raw_vtoc will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL The address in *p refers to a record that is not a valid DSCB. ++ * - ERANGE The cylinder, head, block address lies not within the VTOC. ++ */ ++int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p, ++ struct dscb **dscb) ++{ ++ long long index; ++ char *record; ++ ++ errorlog_clear(rv->log); ++ index = vtocindex_from_cchhb(rv, p); ++ *dscb = NULL; ++ if (!p->cc && !p->hh && !p->b) ++ return 0; ++ /* record zero is part of the track image, but not a dscb */ ++ if (!p->b) ++ return errorlog_add_message( ++ &rv->log, NULL, EINVAL, ++ "raw vtoc: DSCB address is empty\n"); ++ if (index < rv->vtocrecno || index >= rv->vtocindexcount) ++ return errorlog_add_message( ++ &rv->log, NULL, ERANGE, ++ "raw vtoc: DSCB address is outside VTOC\n"); ++ record = rv->vtocindex[vtocindex_from_cchhb(rv, p)]; ++ if (!record) ++ return errorlog_add_message( ++ &rv->log, NULL, EINVAL, ++ "raw vtoc: DSCB address points to nonexistent DSCB\n"); ++ *dscb = (struct dscb *)(record + sizeof(struct eckd_count)); ++ return 0; ++} ++ ++/** ++ * @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_read_rawvtoc(struct dasd *dasd) ++{ ++ unsigned long long vtoctrckno, vtocrecno; ++ unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize; ++ unsigned int vtoc_rec_per_track; ++ unsigned int i; ++ int rc; ++ char *record; ++ struct eckd_count *ecount; ++ 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) { ++ errorlog_add_message( ++ &dasd->log, NULL, rc, ++ "read VTOC: there is no volume label data available\n"); ++ goto cleanup; ++ } ++ /* verify that we have a proper VOL1 label */ ++ if (strncmp(vlabel->volkey, vol1, 4) || ++ strncmp(vlabel->vollbl, vol1, 4)) { ++ rc = EINVAL; ++ errorlog_add_message( ++ &dasd->log, NULL, rc, ++ "read VTOC: volume label is not a VOL1 label\n"); ++ goto cleanup; ++ } ++ ++ /* The label contains the address of the first block of the vtoc. */ ++ vtoctrckno = (unsigned long long) vtoc_get_cyl_from_cchhb(&vlabel->vtoc) ++ * dasd->heads + vtoc_get_head_from_cchhb(&vlabel->vtoc); ++ vtocrecno = vlabel->vtoc.b; ++ ++ /* We do not know how large the VTOC is, yet. So first, we read only ++ * one track of the VTOC to access the format 4 DSCB in the first record ++ * of the VTOC. ++ */ ++ trackdata = memalign(4096, RAWTRACKSIZE); /* page align for O_DIRECT */ ++ if (!trackdata) { ++ rc = ENOMEM; ++ goto cleanup; ++ } ++ rc = dasd_read_tracks(dasd, vtoctrckno, vtoctrckno, trackdata); ++ if (rc) { ++ errorlog_add_message( ++ &dasd->log, dasd->log, rc, ++ "read VTOC: error when reading VTOC start\n"); ++ goto cleanup; ++ } ++ record = NULL; ++ f4 = NULL; ++ i = 0; ++ while (!buffer_get_next_record(trackdata, RAWTRACKSIZE, &record)) { ++ if (i == vtocrecno) { ++ f4 = (format4_label_t *)(record + 8); ++ ecount = (struct eckd_count *)record; ++ break; ++ } ++ ++i; ++ } ++ /* verify that the found record has the expected format */ ++ if (!(f4 && ++ (ecount->kl == 44) && (ecount->dl == 96) && ++ (f4->DS4KEYCD[0] == 0x04) && ++ (f4->DS4KEYCD[43] == 0x04) && ++ (f4->DS4IDFMT == 0xf4))) { ++ rc = EPROTO; ++ errorlog_add_message( ++ &dasd->log, NULL, rc, ++ "read VTOC: could not find format 4 DSCB\n"); ++ goto cleanup; ++ } ++ /* We have found a format 4 label at the position indicated by the ++ * label. ++ * How to determine the size of the VTOC: ++ * - DS4VTOCE contains the VTOC extent, or in other words, lower and ++ * uper boundary of the VTOC ++ * ++ * Searching through the VTOC tracks record by record is tedious, so ++ * we build an array of pointers to the DSCBs, our VTOC index: ++ * Number of entries in the index is the number of tracks times the ++ * number of DSCBS per track plus one for record zero ++ */ ++ lzds_dasd_cchh2trk(dasd, &f4->DS4VTOCE.llimit, &vtoctrack_start); ++ lzds_dasd_cchh2trk(dasd, &f4->DS4VTOCE.ulimit, &vtoctrack_end); ++ vtoc_rec_per_track = (f4->DS4DEVCT.DS4DEVDT + 1); ++ /* A VTOC consists of whole tracks, so the index size is number of ++ * tracks multiplied by records per track ++ */ ++ vtocindexsize = (vtoctrack_end - vtoctrack_start + 1) * ++ vtoc_rec_per_track; ++ ++ rawvtocsize = ((unsigned long long)vtoctrack_end - vtoctrack_start + 1) ++ * RAWTRACKSIZE; ++ ++ f4 = NULL; ++ record = NULL; ++ free(trackdata); ++ trackdata = memalign(4096, rawvtocsize); /* page align for O_DIRECT */ ++ if (!trackdata) { ++ rc = ENOMEM; ++ goto cleanup; ++ } ++ ++ /* read in the full VTOC from disk into memory */ ++ rc = dasd_read_tracks(dasd, vtoctrack_start, vtoctrack_end, trackdata); ++ if (rc) { ++ errorlog_add_message( ++ &dasd->log, dasd->log, rc, ++ "read VTOC: error when reading VTOC\n"); ++ goto cleanup; ++ } ++ ++ rawvtoc->rawdata = trackdata; ++ rawvtoc->rawdatasize = rawvtocsize; ++ rawvtoc->vtoc_rec_per_track = vtoc_rec_per_track; ++ rawvtoc->vtoctrackoffset = vtoctrack_start; ++ rawvtoc->vtocrecno = vtocrecno; ++ rawvtoc->vtocindexcount = vtocindexsize; ++ ++ /* Now parse all VTOC tracks in memory and create an index of ++ * all records (including record 0) ++ */ ++ rawvtoc->vtocindex = malloc(sizeof(char *) * vtocindexsize); ++ if (!rawvtoc->vtocindex) { ++ rc = ENOMEM; ++ goto cleanup; ++ } ++ memset(rawvtoc->vtocindex, 0, (sizeof(char *) * vtocindexsize)); ++ ++ record = NULL; ++ f4 = NULL; ++ i = 0; ++ while (!buffer_get_next_record(trackdata, rawvtocsize, &record)) { ++ /* verify that we do not get too many records */ ++ if (i >= vtocindexsize) { ++ rc = EPROTO; ++ errorlog_add_message( ++ &dasd->log, NULL, rc, ++ "read VTOC: too many records in VTOC\n"); ++ goto cleanup; ++ } ++ rawvtoc->vtocindex[i] = record; ++ ++i; ++ } ++ ++ dasd->rawvtoc = rawvtoc; ++ return 0; ++ ++cleanup: ++ free(rawvtoc->vtocindex); ++ free(trackdata); ++ free(rawvtoc); ++ return rc; ++} ++ ++/** ++ * @param[in] dasd Pointer to the struct dasd we want to get the raw_vtoc from. ++ * @param[out] vtoc Reference to a pointer variable in which a pointer to ++ * the previously read struct raw_vtoc will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL The VTOC has not yet been read. ++ */ ++int lzds_dasd_get_rawvtoc(struct dasd *dasd, struct raw_vtoc **vtoc) ++{ ++ errorlog_clear(dasd->log); ++ *vtoc = dasd->rawvtoc; ++ if (!*vtoc) ++ return EINVAL; ++ else ++ return 0; ++} ++ ++ ++/******************************************************************************/ ++/* HIGH level functions */ ++/******************************************************************************/ ++ ++/** ++ * @param[in] zdsroot Reference to struct zdsroot that the iterator will be ++ * bound to. The iterator will traverse the data sets stored ++ * in this zdsroot. ++ * @param[out] it Reference to a pointer variable in which the newly allocated ++ * structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_zdsroot_alloc_dsiterator(struct zdsroot *zdsroot, ++ struct dsiterator **it) ++{ ++ *it = malloc(sizeof(struct dsiterator)); ++ if (*it) { ++ (*it)->dsi = NULL; ++ (*it)->zdsroot = zdsroot; ++ return 0; ++ } ++ return ENOMEM; ++} ++ ++/** ++ * @param[in] it Pointer to the struct dsiterator that is to be freed. ++ */ ++void lzds_dsiterator_free(struct dsiterator *it) ++{ ++ free(it); ++} ++ ++/** ++ * @param[in] it Reference to the struct dsiterator we use to traverse the ++ * data set list. ++ * @param[out] ds Reference to a pointer variable in which the next ++ * data set in the sequence will be returned. If there ++ * is no next data set, this variable will be set to NULL. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPERM The end of the list has been reached. There is no further dataset. ++ */ ++int lzds_dsiterator_get_next_dataset(struct dsiterator *it, struct dataset **ds) ++{ ++ struct dataset *dstmp; ++ ++ if (!it->dsi) ++ dstmp = util_list_start(it->zdsroot->datasetlist); ++ else ++ dstmp = util_list_next(it->zdsroot->datasetlist, it->dsi); ++ *ds = dstmp; ++ if (!dstmp) ++ return EPERM; ++ it->dsi = dstmp; ++ return 0; ++} ++ ++ ++/** ++ * @param[in] root Reference to struct zdsroot that holds the list of data ++ * sets that this function shall search through. ++ * @param[in] name Name of the data set. ++ * @param[out] ds Reference to a pointer variable in which the found dataset ++ * structure will be returned. If no data set was found, this ++ * variable will be set to NULL ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not internal structure due to lack of memory. ++ * - ENOENT A dataset with the given name was not found. ++ */ ++int lzds_zdsroot_find_dataset(struct zdsroot *root, const char *name, ++ struct dataset **ds) ++{ ++ struct dsiterator *dsit; ++ struct dataset *tempds; ++ int rc; ++ ++ errorlog_clear(root->log); ++ *ds = NULL; ++ rc = lzds_zdsroot_alloc_dsiterator(root, &dsit); ++ if (rc) ++ return ENOMEM; ++ while (!lzds_dsiterator_get_next_dataset(dsit, &tempds)) { ++ if (!strcmp(tempds->name, name)) { ++ *ds = tempds; ++ break; ++ } ++ } ++ lzds_dsiterator_free(dsit); ++ if (!*ds) ++ return ENOENT; ++ return 0; ++} ++ ++/** ++ * @param[in] ds Reference to the struct dataset that the iterator will be ++ * bound to. The iterator will traverse the members stored ++ * in this data set. ++ * @param[out] it Reference to a pointer variable in which the newly allocated ++ * structure will be returned. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ * - EINVAL Failed to allocate a memberiterator because the data set does ++ * not support members (is not a PDS). ++ */ ++int lzds_dataset_alloc_memberiterator(struct dataset *ds, ++ struct memberiterator **it) ++{ ++ if (!ds->memberlist) { ++ *it = NULL; ++ return errorlog_add_message( ++ &ds->log, NULL, EINVAL, ++ "alloc memberiterator: this data set has no members\n"); ++ ++ } ++ *it = malloc(sizeof(struct memberiterator)); ++ if (*it) { ++ (*it)->memberi = NULL; ++ (*it)->ds = ds; ++ return 0; ++ } ++ return ENOMEM; ++} ++ ++/** ++ * @param[in] it Pointer to the struct meberiterator that is to be freed. ++ */ ++void lzds_memberiterator_free(struct memberiterator *it) ++{ ++ free(it); ++} ++ ++/** ++ * @param[out] it Reference to the struct memberiterator we use to traverse ++ * the member list. ++ * @param[out] member Reference to a pointer variable in which the next member ++ * in the sequence will be returned. If there is no next ++ * member, this variable will be set to NULL. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPERM The end of the list has been reached. There is no further dasd. ++ */ ++int lzds_memberiterator_get_next_member(struct memberiterator *it, ++ struct pdsmember **member) ++{ ++ struct pdsmember *memtmp; ++ ++ if (!it->memberi) ++ memtmp = util_list_start(it->ds->memberlist); ++ else ++ memtmp = util_list_next(it->ds->memberlist, it->memberi); ++ *member = memtmp; ++ if (!memtmp) ++ return EPERM; ++ it->memberi = memtmp; ++ return 0; ++} ++ ++ ++/** ++ * @brief Subroutine of raw_vtoc_get_datasetpart_from_dscb ++ * ++ * Check the validity of the extent and copy it to the extent array in the ++ * datasetpart. ++ * @param[in] extent Pointer to the extent that is to be copied. ++ * @param[in] dsp The target datasetpart. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPROTO The extent is not valid. ++ */ ++static int copy_extent_to_datasetpart(extent_t *extent, struct datasetpart *dsp) ++{ ++ /* sanity check: if the extent is valid then make sure that seqno ++ * will not cause us to go beyond the array limits ++ */ ++ if (extent->typeind && extent->seqno >= MAXEXTENTS) ++ return EPROTO; ++ if (extent->typeind) ++ dsp->ext[extent->seqno] = *extent; ++ return 0; ++} ++ ++/** ++ * @brief Subroutine of raw_vtoc_get_datasetpart_from_dscb ++ */ ++static int raw_vtoc_add_extent_error_message(struct raw_vtoc *rv) ++{ ++ return errorlog_add_message( ++ &rv->log, NULL, EPROTO, ++ "vtoc: an extent descriptor is not valid \n"); ++} ++ ++/** ++ * @brief Subroutine of create_dataset_from_dscb ++ * ++ * This function copies the necessary data from a format 1/8 DSCB ++ * into a given datasetpart structure. ++ * @param[in] rv The raw_vtoc that f1 belongs to. ++ * @param[in] f1 The f1/f8 DSCB that the datasetpart is based on. ++ * @param[in] dsp The target datasetpart. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPROTO Invalid data in the DSCB or dependent DSCBs. ++ */ ++static int raw_vtoc_get_datasetpart_from_dscb(struct raw_vtoc *rv, ++ format1_label_t *f1, ++ struct datasetpart *dsp) ++{ ++ format3_label_t *f3; ++ format9_label_t *f9; ++ struct dscb *dscb; ++ int rc, j; ++ ++ errorlog_clear(rv->log); ++ memset(dsp, 0, sizeof(*dsp)); ++ dsp->f1 = f1; ++ ++ /* Find the first format 3 DSCB that is chained format 1 or 8 DSCB. ++ * In a format 8 dscb we will first have one or more format 9 ++ * DSCBs that we need to pass over. ++ */ ++ rc = lzds_raw_vtoc_get_dscb_from_cchhb(rv, &f1->DS1PTRDS, &dscb); ++ while (!rc && dscb && dscb->fmtid == 0xf9) { ++ f9 = (format9_label_t *)dscb; ++ rc = lzds_raw_vtoc_get_dscb_from_cchhb(rv, &f9->DS9PTRDS, ++ &dscb); ++ } ++ if (rc) ++ return errorlog_add_message( ++ &rv->log, rv->log, EPROTO, ++ "vtoc: format 9 DSCB chain not valid \n"); ++ /* We may or may not have a format 3 DSCB */ ++ f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL; ++ ++ /* In any case we have three extents in the f1/8 label itself */ ++ rc = copy_extent_to_datasetpart(&f1->DS1EXT1, dsp); ++ if (rc) ++ return raw_vtoc_add_extent_error_message(rv); ++ rc = copy_extent_to_datasetpart(&f1->DS1EXT2, dsp); ++ if (rc) ++ return raw_vtoc_add_extent_error_message(rv); ++ rc = copy_extent_to_datasetpart(&f1->DS1EXT3, dsp); ++ if (rc) ++ return raw_vtoc_add_extent_error_message(rv); ++ /* now follow the f3 chain */ ++ while (f3) { ++ if (f3->DS3FMTID != 0xf3) ++ return errorlog_add_message( ++ &rv->log, rv->log, EPROTO, ++ "vtoc: format 3 DSCB not valid \n"); ++ for (j = 0; j < 4; ++j) { ++ rc = copy_extent_to_datasetpart(&f3->DS3EXTNT[j], dsp); ++ if (rc) ++ return raw_vtoc_add_extent_error_message(rv); ++ } ++ for (j = 0; j < 9; ++j) { ++ rc = copy_extent_to_datasetpart(&f3->DS3ADEXT[j], dsp); ++ if (rc) ++ return raw_vtoc_add_extent_error_message(rv); ++ } ++ rc = lzds_raw_vtoc_get_dscb_from_cchhb(rv, &f3->DS3PTRDS, ++ (struct dscb **)&f3); ++ if (rc) ++ return errorlog_add_message( ++ &rv->log, rv->log, EPROTO, ++ "vtoc: format 3 DSCB reference not valid\n"); ++ } ++ return 0; ++} ++ ++/** ++ * @brief Subroutine of lzds_zdsroot_extract_datasets_from_dasd ++ * ++ * This functions takes the data of a format 1/8 label, fills in ++ * a given struct dataset and creates exactly one dataset part. ++ * In case of a multi volume data set this part may not be the the ++ * first in the ds->dsp array, but is placed according to its ++ * volume sequence number! ++ * @param[in] dasd The dasd the data set belongs to. ++ * @param[in] f1 The f1/f8 DSCB that the dataset(part) is based on. ++ * @param[in] ds A dataset structure that will be filled with data, ++ * in particular a data set part. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ * - EPROTO Invalid data in the DSCB: An extent is not valid. ++ */ ++static int create_dataset_from_dscb(struct dasd *dasd, format1_label_t *f1, ++ struct dataset *ds) ++{ ++ struct datasetpart *dsp; ++ char *end; ++ int rc; ++ int dspindex; ++ ++ errorlog_clear(dasd->log); ++ memset(ds, 0, sizeof(*ds)); ++ ++ dsp = malloc(sizeof(*dsp)); ++ if (!dsp) ++ return ENOMEM; ++ ++ /* convert EBCDIC fixed length name into ascii 0-terminated string */ ++ strncpy(ds->name, f1->DS1DSNAM, MAXDSNAMELENGTH - 1); ++ vtoc_ebcdic_dec(ds->name, ds->name, MAXDSNAMELENGTH - 1); ++ end = strchr(ds->name, ' '); ++ if (end) ++ *end = 0; ++ ++ rc = raw_vtoc_get_datasetpart_from_dscb(dasd->rawvtoc, f1, dsp); ++ if (rc) { ++ free(dsp); ++ return errorlog_add_message( ++ &dasd->log, dasd->rawvtoc->log, rc, ++ "create data sets: get data set part failed for %s\n", ++ ds->name); ++ } ++ dsp->dasdi = dasd; ++ dspindex = f1->DS1VOLSQ - 1; ++ if (dspindex < 0 || dspindex >= MAXVOLUMESPERDS) { ++ free(dsp); ++ return errorlog_add_message( ++ &dasd->log, NULL, EPROTO, ++ "create data sets: data set sequence number " ++ "out of bounds failed for %s\n", ++ ds->name); ++ } ++ ds->dsp[dspindex] = dsp; ++ ds->dspcount = 1; ++ /* Note: we cannot tell the difference between the first volume of ++ * a multi volume data set and a single volume data set, ++ * so the following is just a first assumption ++ */ ++ if (dspindex == 0) ++ ds->iscomplete = 1; ++ else ++ ds->iscomplete = 0; ++ ++ return 0; ++} ++ ++ ++/** ++ * @brief Subroutine of extract_members_from_track ++ * ++ * Take the information from a pds_member_entry, create a new pdsmember ++ * and add it to the datasets memberlist ++ * @param[in] ds The dataset that the new struct pdsmember will be added to. ++ * @param[in] memberentry The PDS directory entry that describes the member. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++static int dataset_add_member(struct dataset *ds, ++ struct pds_member_entry *memberentry) ++{ ++ char name[9]; ++ char *end; ++ struct pdsmember *member; ++ ++ /* convert name to ascii and truncate trailing spaces */ ++ strncpy(name, memberentry->name, 8); ++ name[8] = 0; ++ vtoc_ebcdic_dec(name, name, 8); ++ end = strchr(name, ' '); ++ if (end) ++ *end = 0; ++ ++ member = malloc(sizeof(*member)); ++ if (!member) ++ return ENOMEM; ++ memset(member, 0, sizeof(*member)); ++ strcpy(member->name, name); ++ member->track = memberentry->track; ++ member->record = memberentry->record; ++ member->is_alias = memberentry->is_alias; ++ util_list_add_tail(ds->memberlist, member); ++ return 0; ++}; ++ ++/** ++ * @brief Helper function that removes and frees all elements in the ++ * member list in a struct dataset. ++ * ++ * @param[in] ds The dataset whose memberlist is to be freed. ++ */ ++static void dataset_free_memberlist(struct dataset *ds) ++{ ++ struct pdsmember *member, *next; ++ ++ if (!ds->memberlist) ++ return; ++ util_list_iterate_safe(ds->memberlist, member, next) { ++ util_list_remove(ds->memberlist, member); ++ errorlog_free(member->log); ++ free(member); ++ } ++ util_list_free(ds->memberlist); ++ ds->memberlist = NULL; ++}; ++ ++/** ++ * @brief Helper function that just checks if the type of an extend ++ * indicates that it contains user data or not. ++ * ++ * @param[in] ext The extent that gets evaluated. ++ * @return 1 if the extent contains user data, 0 otherwise. ++ */ ++static int extent_contains_userdata(extent_t *ext) ++{ ++ return ((ext->typeind == 0x01) || (ext->typeind == 0x81)); ++} ++ ++/** ++ * @brief Subroutine of dataset_member_analysis. ++ * ++ * This function parses one track of a PDS directory and adds all found ++ * members to the dataset. A PDS directory may span more than one track. ++ * The variable dirend is used to indicate the end of the directory. ++ * ++ * @note In case of an error there is no cleanup done for the data set. ++ * ++ * @param[in] trackdata The raw track that contains the PDS directory. ++ * @param[in] ds The dataset the found members will be added to. ++ * @param[out] dirend If the end of the directory is found, dirend is ++ * set to 1, else it is 0. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ * - EPROTO The track layout is not valid. ++ */ ++static int extract_members_from_track(char *trackdata, struct dataset *ds, ++ int *dirend) ++{ ++ char *record, *data; ++ int r; ++ struct eckd_count *ecount; ++ int used_bytes, residual, user_data_size; ++ struct pds_member_entry *member; ++ int rc; ++ ++ *dirend = 0; ++ record = NULL; ++ r = 0; ++ while (!buffer_get_next_record(trackdata, RAWTRACKSIZE, &record)) { ++ /* jump over record zero */ ++ if (r == 0) { ++ ++r; ++ continue; ++ } ++ data = record; ++ ecount = (struct eckd_count *)data; ++ /* sanity check: do key and data length match the format of ++ * a directory record? */ ++ if ((ecount->kl != PDS_DIR_KL) || (ecount->dl != PDS_DIR_DL)) ++ return errorlog_add_message( ++ &ds->log, NULL, EPROTO, ++ "member analysis: directory record layout" ++ " not valid, offset %lu\n", ++ (unsigned long)ecount - (unsigned long)trackdata); ++ data += sizeof(*ecount); ++ /* compare key to directory end token */ ++ if ((*(unsigned long long *)data) == ENDTOKEN) ++ *dirend = 1; ++ data += ecount->kl; ++ /* First element in the data area are two bytes that denote how ++ * may bytes of the data area are used for directory entries. ++ * This number includes the first two bytes. ++ */ ++ used_bytes = (*(unsigned short *)data); ++ residual = used_bytes - sizeof(unsigned short); ++ data += sizeof(unsigned short); ++ /* Loop over directory entries in record */ ++ while (residual > 0) { ++ /* A pseudo directory entry marks directory end */ ++ if ((*(unsigned long long *)data) == ENDTOKEN) { ++ *dirend = 1; /* should already be set */ ++ break; ++ } ++ member = (struct pds_member_entry *)data; ++ rc = dataset_add_member(ds, member); ++ if (rc) ++ return rc; ++ /* A directory entry may contain a user data part ++ * that follows the pds_member_entry structure. ++ */ ++ user_data_size = 2 * member->user_data_count; ++ data += sizeof(*member) + user_data_size; ++ residual -= (sizeof(*member) + user_data_size); ++ } ++ ++r; ++ if (*dirend) ++ break; ++ } ++ return 0; ++} ++ ++/** ++ * @brief Subroutine of lzds_zdsroot_extract_datasets_from_dasd. ++ * ++ * This function checks if a data set is a PDS, analyzes the PDS directory ++ * and creates a corresponding list of struct pdsmember in the dataset. ++ * ++ * @param[in] ds The dataset that is to be analyzed and the found ++ * members will be added to. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ * - EPROTO The track layout is not valid. ++ * - EINVAL An internal error happened. ++ * - EIO An error happened while reading data from disk. ++ */ ++static int dataset_member_analysis(struct dataset *ds) ++{ ++ char *trackdata; ++ unsigned int extstarttrk, extendtrk, currenttrack; ++ int j; ++ int dirend; ++ struct datasetpart *dsp; ++ struct dasd *dasd; ++ struct dasdhandle *dasdh; ++ int rc, rc2; ++ int issupported; ++ ++ errorlog_clear(ds->log); ++ rc2 = 0; ++ /* a partitioned data set has only one volume, so we only need dsp[0] */ ++ dsp = ds->dsp[0]; ++ /* if it is not a partitioned data set, do nothing */ ++ if (!dsp || !(dsp->f1->DS1DSRG1 & 0x02)) ++ return 0; ++ /* do not do member analysis if we do not support the format (PDSE) */ ++ lzds_dataset_get_is_supported(ds, &issupported); ++ if (!issupported) ++ return 0; ++ ++ dasd = dsp->dasdi; ++ ++ dataset_free_memberlist(ds); ++ ds->memberlist = util_list_new(struct pdsmember, list); ++ if (!ds->memberlist) ++ return ENOMEM; ++ ++ /* track buffer must be page aligned for O_DIRECT */ ++ trackdata = memalign(4096, RAWTRACKSIZE); ++ if (!trackdata) ++ return ENOMEM; ++ ++ rc = lzds_dasd_alloc_dasdhandle(dasd, &dasdh); ++ if (rc) ++ goto out1; ++ rc = lzds_dasdhandle_open(dasdh); ++ if (rc) { ++ errorlog_add_message( ++ &ds->log, dasdh->log, rc, ++ "member analysis: could not open dasdhandle\n"); ++ goto out2; ++ } ++ dirend = 0; ++ /* loop over all extents in dataset*/ ++ for (j = 0; j < MAXEXTENTS; ++j) { ++ if (!extent_contains_userdata(&dsp->ext[j])) ++ continue; ++ lzds_dasd_cchh2trk(dasd, &dsp->ext[j].llimit, &extstarttrk); ++ lzds_dasd_cchh2trk(dasd, &dsp->ext[j].ulimit, &extendtrk); ++ currenttrack = extstarttrk; ++ /* loop over tracks in extent */ ++ while (currenttrack <= extendtrk) { ++ rc = lzds_dasdhandle_read_tracks_to_buffer( ++ dasdh, currenttrack, currenttrack, trackdata); ++ if (rc) { ++ errorlog_add_message( ++ &ds->log, dasdh->log, rc, ++ "member analysis: read error\n"); ++ goto out4; ++ } ++ rc = extract_members_from_track(trackdata, ds, &dirend); ++ if (rc) { ++ errorlog_add_message( ++ &ds->log, ds->log, rc, ++ "member analysis: error " ++ "extracting members from track %u\n", ++ currenttrack); ++ goto out4; ++ } ++ currenttrack++; ++ if (dirend) ++ break; ++ } ++ if (dirend) ++ break; ++ } ++ ++ rc = 0; ++ goto out3; ++ ++out4: ++ dataset_free_memberlist(ds); ++out3: ++ rc2 = lzds_dasdhandle_close(dasdh); ++ /* report close error only if we had no read error */ ++ if (rc2 && !rc) { ++ errorlog_add_message( ++ &ds->log, dasdh->log, rc, ++ "member analysis: could not close dasdhandle\n"); ++ rc = rc2; ++ } ++out2: ++ lzds_dasdhandle_free(dasdh); ++out1: ++ free(trackdata); ++ rc = rc ? rc : rc2; ++ return rc; ++} ++ ++/** ++ * @brief Subroutine of zdsroot_merge_dataset ++ * ++ * Merge two dataset structures that are two halves of a multi volume data set. ++ * All datasetparts of the second dataset are copied to the first dataset. ++ * ++ * @param[in] baseds The dataset that the data will be merged into. ++ * @param[in] newds The dataset that will be merged with baseds. ++ * This strucure can be freed after the merge, but do not ++ * free the data set parts it contained, as those belong ++ * to baseds now. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPROTO The data is not mergable because of conflicting entries. ++ */ ++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 */ ++ if (baseds->dsp[k] && newds->dsp[k]) ++ return errorlog_add_message( ++ &baseds->log, NULL, EPROTO, "merge dataset: " ++ "part %d was previously found on device %s\n", ++ k, baseds->dsp[k]->dasdi->device); ++ /* if the new data set has a part that is not present in the ++ * base data set, than copy the dsp pointer to the base ++ */ ++ if (!baseds->dsp[k] && newds->dsp[k]) { ++ /* Each format 1/8 DSCB of a part in a multi volume data ++ * set has a reference to the volume serial of the first ++ * volume. Need to verify that the new data set parts ++ * refer to the correct volume serial in f1->DS1DSSN. ++ * 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; ++ } ++ baseds->dsp[k] = newds->dsp[k]; ++ baseds->dspcount++; ++ ++ } ++ } ++ /* check for completeness: ++ * If element (dspcount - 1) exists and is the last part in a multi ++ * volume data set, then all other parts must have been found as well. ++ */ ++ dspcount = baseds->dspcount; ++ if (baseds->dsp[dspcount - 1] && ++ (baseds->dsp[dspcount - 1]->f1->DS1DSIND & 0x80)) ++ baseds->iscomplete = 1; ++ else ++ baseds->iscomplete = 0; ++ /* The last statement is only true for a correct multi volume data set. ++ * Since the data on the DASDs may be incorrect and we will rely later ++ * on the fact that the first dspcount elements of the dsp array are ++ * valid, we must make sure that they are all filled. ++ */ ++ if (baseds->iscomplete) ++ for (l = 0; l < baseds->dspcount; ++l) ++ if (!baseds->dsp[l]) { ++ baseds->iscomplete = 0; ++ return errorlog_add_message( ++ &baseds->log, NULL, EPROTO, ++ "merge dataset: inconsistent data set" ++ " part list at index %d\n", l); ++ } ++ ++ return 0; ++} ++ ++/** ++ * @brief Subroutine of lzds_zdsroot_extract_datasets_from_dasd ++ * ++ * Takes the data from newds and merges it with a matching dataset in ++ * root. If no matching dataset exists yet, a new struct dataset is ++ * created, so that the caller of this function can release newds in ++ * any case. ++ * It is important to note that while newds is just a temporary ++ * structure that can be released after the function returns, the ++ * elements and structures that are contained by newds (e.g the ++ * datasetparts) are transferred to the struct dataset in root and must ++ * not be released. ++ * ++ * @param[in] root The zdsroot that the dataset will be merged into. ++ * @param[in] newds The dataset that will be merged. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - EPROTO The data is not mergable because of conflicting entries. ++ */ ++static int zdsroot_merge_dataset(struct zdsroot *root, struct dataset *newds) ++{ ++ struct dataset *rootds; ++ int rc; ++ ++ /* first, try to find a matching data set in the old list */ ++ rc = lzds_zdsroot_find_dataset(root, newds->name, &rootds); ++ if (!rc) { /* match found */ ++ rc = dataset_merge_dataset(rootds, newds); ++ if (rc) ++ return errorlog_add_message( ++ &root->log, rootds->log, rc, ++ "merge dataset: " ++ "merge with existing data set failed\n"); ++ } else if (rc == ENOENT) { /* no match found */ ++ rootds = malloc(sizeof(*rootds)); ++ if (!rootds) ++ return ENOMEM; ++ memcpy(rootds, newds, sizeof(*rootds)); ++ util_list_add_tail(root->datasetlist, rootds); ++ } else ++ return rc; ++ return 0; ++} ++ ++/** ++ * This function finds all data set descriptions in the VTOC of the ++ * dasd and creates respective struct dataset representations. These ++ * struct dataset are stored in the zdsroot and can later be traversed ++ * using a dsiterator. In case that it finds a dataset that is ++ * already present in the zdsroot, it verifies that both are parts of ++ * the same multivolume data set and then merges the new data with the ++ * existing struct dataset. If the conflicting data sets are indeed ++ * individual data sets and not parts of a single one, the function ++ * returns an error. ++ * ++ * @param[in] root The zdsroot that the dataset will be merged into. ++ * @param[in] dasd The datasets found in this dasd will be merged. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ * - EPROTO The data is not mergable because of conflicting entries, ++ * or invalid data in the VTOC of the dasd. ++ */ ++int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root, ++ struct dasd *dasd) ++{ ++ format1_label_t *f1; ++ struct dscb *dscb; ++ struct dscbiterator *it; ++ int rc; ++ struct dataset tmpds; ++ int i; ++ ++ errorlog_clear(root->log); ++ memset(&tmpds, 0, sizeof(tmpds)); ++ rc = lzds_raw_vtoc_alloc_dscbiterator(dasd->rawvtoc, &it); ++ if (rc) ++ return ENOMEM; ++ while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { ++ if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8) { ++ f1 = (format1_label_t *)dscb; ++ rc = create_dataset_from_dscb(dasd, f1, &tmpds); ++ if (rc) { ++ errorlog_add_message( ++ &root->log, dasd->log, rc, ++ "extract data sets: " ++ "creating dataset failed for %s\n", ++ dasd->device); ++ break; ++ } ++ rc = dataset_member_analysis(&tmpds); ++ if (rc) { ++ errorlog_add_message( ++ &root->log, tmpds.log, rc, ++ "extract data sets: " ++ "member analysis failed for %s\n", ++ tmpds.name); ++ break; ++ } ++ rc = zdsroot_merge_dataset(root, &tmpds); ++ if (rc) { ++ errorlog_add_message( ++ &root->log, root->log, rc, ++ "extract data sets: " ++ "merge dataset failed for %s\n", ++ tmpds.name); ++ break; ++ } ++ } ++ } ++ if (rc) { ++ dataset_free_memberlist(&tmpds); ++ for (i = 0; i < MAXVOLUMESPERDS; ++i) ++ free(tmpds.dsp[i]); ++ errorlog_free(tmpds.log); ++ } ++ lzds_dscbiterator_free(it); ++ return rc; ++} ++ ++/** ++ * @brief Subroutine of lzds_dataset_get_size_in_tracks ++ * ++ * Computes the number of tracks in a given extent. ++ * Returns 0 for anything but user data. ++ * @param[in] ext The extent we want to know the size of. ++ * @param[in] dasd The dasd that the extent is located on. ++ * @return Number of tracks the extent contains ++ */ ++static unsigned int get_extent_size_in_tracks(extent_t *ext, struct dasd *dasd) ++{ ++ unsigned int starttrck, endtrck; ++ ++ if (!extent_contains_userdata(ext)) ++ return 0; ++ ++ lzds_dasd_cchh2trk(dasd, &ext->llimit, &starttrck); ++ lzds_dasd_cchh2trk(dasd, &ext->ulimit, &endtrck); ++ ++ return endtrck - starttrck + 1; ++} ++ ++/** ++ * @param[in] ds The dataset we we want to know the size of. ++ * @param[out] tracks Reference to a return buffer for the number of tracks. ++ */ ++void lzds_dataset_get_size_in_tracks(struct dataset *ds, ++ unsigned long long *tracks) ++{ ++ unsigned long long sumtracks; ++ int i, j; ++ ++ *tracks = 0; ++ sumtracks = 0; ++ for (i = 0; i < MAXVOLUMESPERDS; ++i) ++ if (ds->dsp[i]) ++ for (j = 0; j < MAXEXTENTS; ++j) ++ sumtracks += get_extent_size_in_tracks( ++ &ds->dsp[i]->ext[j], ds->dsp[i]->dasdi); ++ *tracks = sumtracks; ++} ++ ++/** ++ * @param[in] member The PDS member we want to know the name of. ++ * @param[out] name Reference to a pointer variable in which a pointer to ++ * the name string will be returned. ++ */ ++void lzds_pdsmember_get_name(struct pdsmember *member, char **name) ++{ ++ *name = member->name; ++} ++ ++/** ++ * @param[in] ds The dataset we want to know the name of. ++ * @param[out] name Reference to a pointer variable in which a pointer to ++ * the name string will be returned. ++ */ ++void lzds_dataset_get_name(struct dataset *ds, char **name) ++{ ++ *name = ds->name; ++} ++ ++/** ++ * @param[in] ds Is this dataset a PDS? ++ * @param[out] ispds Reference to a pointer variable in which ++ * 1 (true) or 0 (false) is returned. ++ */ ++void lzds_dataset_get_is_PDS(struct dataset *ds, int *ispds) ++{ ++ ++ if (ds->dsp[0]->f1->DS1DSRG1 & 0x02) /* is PDS */ ++ *ispds = 1; ++ else ++ *ispds = 0; ++} ++ ++/** ++ * The returned DSCB belongs always to the first volume of a data set. ++ * ++ * @param[in] ds The dataset we want to know the DSCB of. ++ * @param[out] f1 Reference to a pointer variable in which a pointer to ++ * the format 1 DSCB will be returned. ++ */ ++void lzds_dataset_get_format1_dscb(struct dataset *ds, format1_label_t **f1) ++{ ++ *f1 = ds->dsp[0]->f1; ++} ++ ++/** ++ * @param[in] ds Is this dataset complete? ++ * @param[out] iscomplete Reference to a pointer variable in which ++ * 1 (true) or 0 (false) is returned. ++ */ ++void lzds_dataset_get_is_complete(struct dataset *ds, int *iscomplete) ++{ ++ *iscomplete = ds->iscomplete; ++} ++ ++/** ++ * @param[in] ds Is this dataset supported? ++ * @param[out] issupported Reference to a pointer variable in which ++ * 1 (true) or 0 (false) is returned. ++ */ ++void lzds_dataset_get_is_supported(struct dataset *ds, int *issupported) ++{ ++ int complete, org_supported, format_supported, not_ext_fmt; ++ char DS1RECFM; ++ ++ if (!ds->dsp[0]) { ++ *issupported = 0; ++ return; ++ } ++ /* do we have all parts of the data set? */ ++ lzds_dataset_get_is_complete(ds, &complete); ++ ++ /* is this a supported organisation (PS or PDS)?*/ ++ org_supported = 0; ++ if ((ds->dsp[0]->f1->DS1DSRG1 & 0x40) || /* PS */ ++ (ds->dsp[0]->f1->DS1DSRG1 & 0x02)) /* PDS */ ++ org_supported = 1; ++ /* extended format datasets are not supported */ ++ not_ext_fmt = 0; ++ if (!(ds->dsp[0]->f1->DS1SMSFG & 0x0C)) ++ not_ext_fmt = 1; ++ /* fixed, variable or undefined length records are supported */ ++ DS1RECFM = ds->dsp[0]->f1->DS1RECFM; ++ format_supported = 0; ++ if (DS1RECFM & 0xC0) ++ format_supported = 1; ++ /* track overflow (legacy) is not supported */ ++ if ((DS1RECFM & 0x20)) ++ format_supported = 0; ++ /* all other RECFM flags are modifiers of the above and are supported */ ++ *issupported = complete && org_supported && format_supported ++ && not_ext_fmt; ++ return; ++} ++ ++/** ++ * @param[in] ds The dataset that is searched for the member. ++ * @param[in] membername The name of the member (ASCII string). ++ * @param[out] member Reference to a pointer variable in which the found ++ * pdsmember is returned. If no member is found, this ++ * is set to NULL. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - ENOENT No matching member was found. ++ */ ++int lzds_dataset_get_member_by_name(struct dataset *ds, char *membername, ++ struct pdsmember **member) ++{ ++ struct memberiterator *it; ++ struct pdsmember *tmpmember; ++ int rc; ++ ++ errorlog_clear(ds->log); ++ *member = NULL; ++ rc = lzds_dataset_alloc_memberiterator(ds, &it); ++ if (rc) ++ return ENOMEM; ++ while (!lzds_memberiterator_get_next_member(it, &tmpmember)) { ++ if (!strcmp(tmpmember->name, membername)) { ++ *member = tmpmember; ++ break; ++ } ++ } ++ lzds_memberiterator_free(it); ++ if (!*member) ++ return ENOENT; ++ return 0; ++} ++ ++/** ++ * @param[in] dsh Pointer to structure that is to be freed. ++ */ ++void lzds_dshandle_free(struct dshandle *dsh) ++{ ++ int i; ++ ++ if (!dsh) ++ return; ++ for (i = 0; i < MAXVOLUMESPERDS; ++i) ++ if (dsh->dasdhandle[i]) ++ lzds_dasdhandle_free(dsh->dasdhandle[i]); ++ free(dsh->databuffer); ++ free(dsh->rawbuffer); ++ if (dsh->seekbuf) ++ free(dsh->seekbuf); ++ errorlog_free(dsh->log); ++ free(dsh); ++} ++ ++/** ++ * @param[in] ds The dataset we want to read from. ++ * @param[in] tracks_per_frame The number of tracks that the internal buffers ++ * can hold. If 0, then the default value 128 is used. ++ * @param[out] dsh Reference to a pointer variable which will be used ++ * to store the new dshandle. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_dataset_alloc_dshandle(struct dataset *ds, ++ unsigned int tracks_per_frame, ++ struct dshandle **dsh) ++{ ++ struct dshandle *dshtmp; ++ int i, rc; ++ ++ dshtmp = malloc(sizeof(*dshtmp)); ++ if (!dshtmp) ++ return ENOMEM; ++ memset(dshtmp, 0, sizeof(*dshtmp)); ++ for (i = 0; i < ds->dspcount; ++i) { ++ rc = lzds_dasd_alloc_dasdhandle(ds->dsp[i]->dasdi, ++ &dshtmp->dasdhandle[i]); ++ if (rc) { ++ lzds_dshandle_free(dshtmp); ++ return rc; ++ } ++ } ++ if (tracks_per_frame) ++ dshtmp->tracks_per_frame = tracks_per_frame; ++ else ++ dshtmp->tracks_per_frame = TRACK_BUFFER_DEFAULT; ++ dshtmp->rawbufmax = dshtmp->tracks_per_frame * RAWTRACKSIZE; ++ /* track buffer must be page aligned for O_DIRECT */ ++ dshtmp->rawbuffer = memalign(4096, dshtmp->rawbufmax); ++ if (!dshtmp->rawbuffer) { ++ lzds_dshandle_free(dshtmp); ++ return ENOMEM; ++ } ++ ++ dshtmp->databufmax = dshtmp->tracks_per_frame * MAXRECSIZE; ++ dshtmp->databuffer = malloc(dshtmp->databufmax); ++ if (!dshtmp->databuffer) { ++ lzds_dshandle_free(dshtmp); ++ return ENOMEM; ++ } ++ ++ dshtmp->ds = ds; ++ *dsh = dshtmp; ++ return 0; ++} ++ ++/** ++ * The number of user data bytes per track is not predictable as record ++ * sizes and number of records per track may vary. Seeking forward will ++ * always require us to read all the data between the current position ++ * and the seek target. To improve performance of seeking backwards ++ * we can buffer previous positions in the data set. ++ * For a given seek buffer size and the known number of tracks of the ++ * data set, we can compute how many track frames we need to skip if ++ * we and to store track frames in regular intervals. ++ * ++ * @param[in] dsh The dshandle we want to modify. ++ * @param[in] seek_buffer_size The maximum number of bytes to be allocated ++ * for the seek buffer. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate structure due to lack of memory. ++ */ ++int lzds_dshandle_set_seekbuffer(struct dshandle *dsh, ++ unsigned long long seek_buffer_size) ++{ ++ unsigned long long totaltracks; ++ size_t entries, frames; ++ unsigned int extents, skip; ++ struct dataset *ds; ++ int i, j; ++ unsigned long long buf_count; ++ ++ errorlog_clear(dsh->log); ++ if (dsh->seekbuf) ++ free(dsh->seekbuf); ++ dsh->seekbuf = NULL; ++ dsh->seek_count = 0; ++ dsh->seek_current = 0; ++ dsh->skip = 0; ++ ++ if (!seek_buffer_size) ++ return 0; ++ ++ ds = dsh->ds; ++ lzds_dataset_get_size_in_tracks(ds, &totaltracks); ++ ++ /* compute the total number of extents */ ++ extents = 0; ++ for (i = 0; i < ds->dspcount; ++i) ++ for (j = 0; j < MAXEXTENTS; ++j) ++ if (ds->dsp[i]->ext[j].typeind != 0x00) ++ ++extents; ++ ++ entries = seek_buffer_size / sizeof(struct seekelement); ++ ++ /* track frames at the end of an extent may be shorter, ++ * increasing the maximum number of frames we need to read */ ++ frames = (totaltracks / dsh->tracks_per_frame) + 1 + extents; ++ skip = (frames / entries) + 1; ++ buf_count = (frames / skip) + 1; ++ ++ dsh->seekbuf = malloc(buf_count * sizeof(struct seekelement)); ++ if (!dsh->seekbuf) ++ return ENOMEM; ++ memset(dsh->seekbuf, 0, buf_count * sizeof(struct seekelement)); ++ dsh->seek_count = buf_count; ++ dsh->skip = skip; ++ return 0; ++} ++ ++ ++/** ++ * If dsh points to a partitioned data set, the library needs to know ++ * which member of that PDS should be read. So this function must be ++ * called before lzds_dshandle_open. This setting cannot be changed ++ * for open dsh, so this function must not be used after ++ * lzds_dshandle_open, unless the dsh has been closed with ++ * lzds_dsh_close again. ++ * ++ * @pre The dsh must not be open when this function is called. ++ * ++ * @param[in] dsh The dshandle we want to modify. ++ * @param[in] membername The name of the member that shall be read via ++ * this handle. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOMEM Could not allocate internal structure due to lack of memory. ++ * - ENOENT No matching member was found. ++ * - EBUSY The handle is already open. ++ * - EINVAL The data set is not a PDS. ++ */ ++int lzds_dshandle_set_member(struct dshandle *dsh, char *membername) ++{ ++ int ispds, rc; ++ struct pdsmember *member; ++ ++ errorlog_clear(dsh->log); ++ if (dsh->is_open) ++ return errorlog_add_message( ++ &dsh->log, NULL, EBUSY, ++ "dshandle: cannot set member while handle is open\n"); ++ dsh->member = NULL; ++ lzds_dataset_get_is_PDS(dsh->ds, &ispds); ++ if (!ispds) ++ return errorlog_add_message( ++ &dsh->log, NULL, EINVAL, ++ "dshandle: cannot set member, not a PDS\n"); ++ ++ rc = lzds_dataset_get_member_by_name(dsh->ds, membername, &member); ++ if (rc) ++ return errorlog_add_message( ++ &dsh->log, NULL, rc, ++ "dshandle: could not find member %s in dataset %s\n", ++ membername, dsh->ds->name); ++ ++ dsh->member = member; ++ return 0; ++} ++ ++/** ++ * @param[in] dsh The dshandle that we want to know the member of. ++ * @param[out] member Reference to a pointer variable in which the found ++ * pdsmember is returned. If no member has been set ++ * before, this is set to NULL. ++ */ ++void lzds_dshandle_get_member(struct dshandle *dsh, struct pdsmember **member) ++{ ++ *member = dsh->member; ++} ++ ++/** ++ * @pre The dsh must not be open when this function is called. ++ * ++ * @param[in] dsh The dshandle we want to modify. ++ * @param[in] keepRDW Set this to 1 to enable the keep RDW feature or ++ * 0 to disable it. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EBUSY The handle is already open. ++ */ ++int lzds_dshandle_set_keepRDW(struct dshandle *dsh, int keepRDW) ++{ ++ errorlog_clear(dsh->log); ++ if (dsh->is_open) ++ return errorlog_add_message( ++ &dsh->log, NULL, EBUSY, ++ "dshandle: cannot set RDW while handle is open\n"); ++ dsh->keepRDW = keepRDW; ++ return 0; ++} ++ ++/** ++ * @param[in] dsh The dshandle that we want to know the member of. ++ * @param[out] keepRDW Reference to a variable in which the previously ++ * set keepRDW value is returned. ++ */ ++void lzds_dshandle_get_keepRDW(struct dshandle *dsh, int *keepRDW) ++{ ++ *keepRDW = dsh->keepRDW; ++} ++ ++/** ++ * @brief Helper function that initializes the given handle so that it ++ * points to the beginning of the dataset or member. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPROTO The dataset data is inconsistent. ++ */ ++static int initialize_buffer_positions_for_first_read(struct dshandle *dsh) ++{ ++ ++ unsigned long long tracksum, extentsize; ++ unsigned int starttrck, endtrck; ++ int j; ++ ++ /* make sure that read knows that we have no ready data in our buffer */ ++ dsh->bufpos = 0; ++ dsh->databufsize = 0; ++ dsh->databufoffset = 0; ++ dsh->eof_reached = 0; ++ ++ /* we need to set the bufendtrk and sequence number so, ++ * that the current track buffer seems to end with the ++ * track that comes before the first track of the ++ * data set or member ++ */ ++ ++ /* When we read the first track frame this will be incremented to 0 */ ++ dsh->frameno = -1; ++ ++ /* We allways start with data set part 0. Partitioned ++ * data sets have only one part, so this correct for ++ * both partitioned and non partitioned data sets. ++ */ ++ dsh->dsp_no = 0; ++ ++ /* for a non partitioned data set we just need to set the ++ * extentsequence number to -1 so read will start with the ++ * first track of extent number 0 ++ */ ++ if (!dsh->member) { ++ dsh->ext_seq_no = -1; ++ dsh->bufstarttrk = 0; ++ dsh->bufendtrk = 0; ++ dsh->extstarttrk = 0; ++ dsh->extendtrk = 0; ++ return 0; ++ } ++ ++ /* sanity check: a partitioned data set cannot be a multi volume data ++ * set. ++ */ ++ if (dsh->ds->dspcount != 1) ++ return errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "initialize read buffer: dataset %s is inconsistent," ++ " PDS must not span more than one volume\n", ++ dsh->ds->name); ++ /* For a partitioned data set we need to find the correct start ++ * track and point the current buffer just before it. ++ * As we always need to read full tracks, any additional ++ * record offset will be set explicitly and handled during ++ * track interpretation. ++ */ ++ dsh->startrecord = dsh->member->record; ++ ++ /* member->track is an offset based on the start of the data set ++ * I will have to add up extents until I have got the right number ++ * of tracks ++ */ ++ tracksum = 0; ++ /* Note: No need to loop over all data set parts, a PDS has only one */ ++ for (j = 0; j < MAXEXTENTS; ++j) { ++ if (!extent_contains_userdata(&dsh->ds->dsp[0]->ext[j])) ++ continue; ++ lzds_dasd_cchh2trk(dsh->ds->dsp[0]->dasdi, ++ &dsh->ds->dsp[0]->ext[j].llimit, &starttrck); ++ lzds_dasd_cchh2trk(dsh->ds->dsp[0]->dasdi, ++ &dsh->ds->dsp[0]->ext[j].ulimit, &endtrck); ++ extentsize = endtrck - starttrck + 1; ++ ++ /* If offset in the extent (member->track - tracksum) == 0, ++ * then we must set the dsh buffer to the end of the previous ++ * extent, so that rdf_read will start with the first track ++ * of the next extent. ++ * However, since rdf_read checks for bufendtrk < extendtrk ++ * we can set both to 0 and do not need a special case for the ++ * first extend. ++ */ ++ if (dsh->member->track == tracksum) { ++ dsh->ext_seq_no = j - 1; ++ dsh->bufendtrk = 0; ++ dsh->extendtrk = 0; ++ break; ++ } ++ /* If the offset is within the current extent an not the ++ * special case above, then we can need to adjust the dsh so, ++ * as if we have just already read data up to the track before ++ * our target track ++ */ ++ if (dsh->member->track < tracksum + extentsize) { ++ dsh->ext_seq_no = j; ++ dsh->extstarttrk = starttrck; ++ dsh->extendtrk = endtrck; ++ dsh->bufstarttrk = dsh->extstarttrk; ++ dsh->bufendtrk = dsh->bufstarttrk + ++ (dsh->member->track - tracksum) - 1; ++ break; ++ } ++ tracksum += extentsize; ++ } ++ return 0; ++} ++ ++ ++/** ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ */ ++void lzds_dshandle_close(struct dshandle *dsh) ++{ ++ int i; ++ for (i = 0; i < MAXVOLUMESPERDS; ++i) ++ if (dsh->dasdhandle[i]) ++ lzds_dasdhandle_close(dsh->dasdhandle[i]); ++ dsh->is_open = 0; ++} ++ ++ ++/** ++ * This makes the data set context ready for read operations. ++ * All settings on the dsh must be done before it is opened. ++ * @pre For a partitioned data set a member must be set before ++ * this function is called. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - ENOTSUP The dataset is of a type that is not supported. ++ * - EINVAL Tried to open a PDS without setting a member before.. ++ * - EIO Could not open underlying device. ++ */ ++int lzds_dshandle_open(struct dshandle *dsh) ++{ ++ int i, rc; ++ int ispds, issupported; ++ ++ /* sanity check: Open will fail if the data set type is not supported. ++ * We do this check here and not during dshandle creation, as it may ++ * depend on settings on the dshandle that the user has to make ++ * between creation and open. ++ */ ++ errorlog_clear(dsh->log); ++ lzds_dataset_get_is_supported(dsh->ds, &issupported); ++ if (!issupported) ++ return errorlog_add_message( ++ &dsh->log, ++ NULL, ENOTSUP, ++ "data set open: data set %s is not supported\n", ++ dsh->ds->name); ++ lzds_dataset_get_is_PDS(dsh->ds, &ispds); ++ if (ispds && !dsh->member) ++ return errorlog_add_message( ++ &dsh->log, ++ NULL, EINVAL, ++ "data set open: a member must be set" ++ " before PDS %s can be opened\n", dsh->ds->name); ++ rc = initialize_buffer_positions_for_first_read(dsh); ++ if (rc) ++ return errorlog_add_message( ++ &dsh->log, ++ dsh->log, rc, ++ "data set open: error when initializing buffers" ++ " for data set %s\n", dsh->ds->name); ++ for (i = 0; i < dsh->ds->dspcount; ++i) { ++ rc = lzds_dasdhandle_open(dsh->dasdhandle[i]); ++ if (rc) { ++ errorlog_add_message( ++ &dsh->log, ++ dsh->dasdhandle[i]->log, rc, ++ "data set open: error opening DASD " ++ "for data set %s\n", dsh->ds->name); ++ lzds_dshandle_close(dsh); ++ return rc; ++ } ++ } ++ dsh->is_open = 1; ++ return 0; ++} ++ ++/** ++ * @brief subroutine of dshandle_extract_data_from_trackbuffer ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] rec Pointer to the raw record. ++ * @param[in] targetdata Pointer to the data buffer. ++ * @return Number of copied data bytes on success, ++ * otherwise one of the following (negative) error codes: ++ * - -EPROTO The record is malformed. ++ */ ++static ssize_t parse_fixed_record(struct dshandle *dsh, ++ char *rec, char *targetdata) ++{ ++ struct eckd_count *ecount; ++ ++ ecount = (struct eckd_count *)rec; ++ /* Make sure that we do not copy data beyond the end of ++ * the data buffer ++ */ ++ if ((unsigned long)targetdata + ecount->dl > ++ (unsigned long)dsh->databuffer + dsh->databufmax) ++ return - errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "fixed record to long for target buffer\n"); ++ memcpy(targetdata, (rec + sizeof(*ecount) + ecount->kl), ecount->dl); ++ return ecount->dl; ++} ++ ++/** ++ * @brief subroutine of dshandle_extract_data_from_trackbuffer ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] rec Pointer to the raw record. ++ * @param[in] targetdata Pointer to the data buffer. ++ * @param[in] keepRDW Flag that specifies if the RDW should be copied to ++ * the data buffer or or not. ++ * @return Number of copied data bytes on success, ++ * otherwise one of the following (negative) error codes: ++ * - -EPROTO The record is malformed. ++ */ ++static ssize_t parse_variable_record(struct dshandle *dsh, char *rec, ++ char *targetdata, int keepRDW) ++{ ++ struct eckd_count *ecount; ++ unsigned int blocklength, segmentlength, residual; ++ char *data; ++ struct segment_header *blockhead; ++ struct segment_header *seghead; ++ size_t totaldatalength; ++ ++ /* We must not rely on the data in rec, as it was read from disk and ++ * may be broken. Wherever we interprete the data we must have sanity ++ * checks. ++ */ ++ ecount = (struct eckd_count *)rec; ++ totaldatalength = 0; ++ /* An empty record is expected at the end of dataset or member */ ++ if (ecount->dl == 0) ++ return 0; ++ /* If the data area is not zero but to small to contain a segment header ++ * then the record contents cannot be valid. ++ */ ++ if (ecount->dl < sizeof(struct segment_header)) ++ return - errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "variable record parser: record length to small\n"); ++ data = (rec + sizeof(*ecount) + ecount->kl); ++ blockhead = (struct segment_header *)data; ++ blocklength = blockhead->length; ++ /* If the length in the block descriptor is 0, then the block contains ++ * no data. Not sure if this is a valid case, but we tolerate it. */ ++ if (!blocklength) ++ return totaldatalength; ++ /* If blocklength is to small to contain the block descriptor or to ++ * large to fit in the data area, then the block descriptor is broken */ ++ if ((blocklength < sizeof(*blockhead)) || (blocklength > ecount->dl)) ++ return - errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "variable record parser: block length to small\n"); ++ data += sizeof(*blockhead); ++ residual = blocklength - sizeof(*blockhead); ++ while (residual) { ++ seghead = (struct segment_header *)data; ++ segmentlength = seghead->length; ++ if (seghead->nullsegment || !segmentlength) { ++ /* null segment found -> end of data in block */ ++ return totaldatalength; ++ } ++ /* If segmentlength is to small to contain the record descriptor ++ * descriptor or to large to fit in the residual data area, then ++ * the record descriptor is broken ++ */ ++ if ((residual < segmentlength) || ++ (segmentlength < sizeof(*seghead))) ++ return - errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "variable record parser: segment length %d " ++ "inconsistent at offset %lu\n", ++ segmentlength, ++ (unsigned long)seghead - (unsigned long)rec); ++ residual -= segmentlength; ++ if (!keepRDW) { ++ data += sizeof(*seghead); ++ segmentlength -= sizeof(*seghead); ++ } ++ /* Make sure that we do not copy data beyond the end of ++ * the data buffer ++ */ ++ if ((unsigned long)targetdata + segmentlength > ++ (unsigned long)dsh->databuffer + dsh->databufmax) ++ return - errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "variable record parser: " ++ "record to long for target buffer\n"); ++ memcpy(targetdata, data, segmentlength); ++ targetdata += segmentlength; ++ totaldatalength += segmentlength; ++ data += segmentlength; ++ } ++ return totaldatalength; ++} ++ ++/** ++ * @brief subroutine of lzds_dshandle_read ++ * ++ * Parses the raw track buffer in dsh and copies the user data to ++ * the databuffer in dsh. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPROTO The raw track data is malformed. ++ */ ++static int dshandle_extract_data_from_trackbuffer(struct dshandle *dsh) ++{ ++ char *track; ++ size_t i, trckcount; ++ struct eckd_count *ecount; ++ char *rawdata, *targetdata; ++ unsigned int record; ++ char DS1RECFM; ++ ssize_t tdsize; ++ ++ DS1RECFM = dsh->ds->dsp[0]->f1->DS1RECFM; ++ trckcount = dsh->rawbufsize / RAWTRACKSIZE; ++ track = dsh->rawbuffer; ++ targetdata = dsh->databuffer; ++ dsh->databufsize = 0; ++ /* Record zero is not part of the regular data, so I must not copy its ++ * data. In case of a PDS member, we may need to skip a few extra ++ * records on the first track. In this case startrecord is already set ++ * and will be reset to 1 after the first track has been read. ++ */ ++ if (!dsh->startrecord) ++ dsh->startrecord = 1; ++ for (i = 0; i < trckcount && !dsh->eof_reached; ++i) { ++ record = 0; ++ rawdata = track; ++ while (!dsh->eof_reached) { ++ tdsize = 0; ++ if (record >= dsh->startrecord) { ++ /* fixed or undefined record size */ ++ if ((DS1RECFM & 0x80)) ++ tdsize = parse_fixed_record(dsh, ++ rawdata, ++ targetdata); ++ /* variable records */ ++ if (!(DS1RECFM & 0x80) && (DS1RECFM & 0x40)) ++ tdsize = parse_variable_record(dsh, ++ rawdata, ++ targetdata, ++ dsh->keepRDW); ++ if (tdsize < 0) ++ return errorlog_add_message( ++ &dsh->log, dsh->log, EPROTO, ++ "data extraction: error at " ++ "record %u, offset %lu\n", ++ record, ++ (unsigned long)rawdata ++ - (unsigned long)dsh->rawbuffer); ++ targetdata += tdsize; ++ dsh->databufsize += tdsize; ++ } ++ ecount = (struct eckd_count *)rawdata; ++ rawdata += sizeof(*ecount) + ecount->kl + ecount->dl; ++ /* An empty record marks the end of a member / data set ++ * We need to take startrecord into account or we might ++ * find the end marker of the previous member. ++ */ ++ if ((record >= dsh->startrecord) && ++ (!ecount->kl) && (!ecount->dl)) ++ dsh->eof_reached = 1; ++ ++record; ++ if ((*(unsigned long long *)rawdata) == ENDTOKEN) ++ break; ++ if ((unsigned long)rawdata >= ++ (unsigned long)track + RAWTRACKSIZE) ++ return errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "data extraction: run over end of" ++ " track buffer\n"); ++ } ++ dsh->startrecord = 1; ++ track += RAWTRACKSIZE; ++ } ++ return 0; ++} ++ ++ ++/** ++ * @brief subroutine of lzds_dshandle_read ++ * ++ * Find the next range of extents and prepare dsh for the next read. ++ * The return value indicates whether there is more data to read or not. ++ * ++ * @pre: For the first call to this function, dsh should be set to the ++ * last track before the first track to read. ++ * If the first track to read is the first track in the dataset ++ * then set dsh->ext_seq_no to -1. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * ++ * @return ++ * 0 when there is no further raw data available, ++ * 1 when there is more data available and dsh is prepared ++ */ ++static int dshandle_prepare_for_next_read_tracks(struct dshandle *dsh) ++{ ++ int found, dsp_no, ext_seq_no; ++ ++ /* If there are still unread tracks in the current extent, we just need ++ * to point dsh to the next range of tracks ++ */ ++ if (dsh->bufendtrk < dsh->extendtrk) { ++ dsh->bufstarttrk = dsh->bufendtrk + 1; ++ dsh->bufendtrk = dsh->bufstarttrk + ++ (dsh->rawbufmax / RAWTRACKSIZE) - 1; ++ dsh->bufendtrk = min(dsh->bufendtrk, dsh->extendtrk); ++ dsh->rawbufsize = (dsh->bufendtrk - dsh->bufstarttrk + 1) ++ * RAWTRACKSIZE; ++ dsh->databufoffset = dsh->databufoffset + dsh->databufsize; ++ dsh->databufsize = 0; ++ dsh->bufpos = 0; ++ dsh->frameno++; ++ return 1; ++ } ++ /* There are no more tracks left in the current extent. ++ * Loop over data set parts and extends in these parts until a valid ++ * extent is found or the end of the data set is reached ++ */ ++ ext_seq_no = dsh->ext_seq_no; ++ dsp_no = dsh->dsp_no; ++ found = 0; ++ while (!found) { ++ ++ext_seq_no; ++ if (ext_seq_no >= MAXEXTENTS) { ++ ext_seq_no = 0; ++ ++dsp_no; ++ } ++ if (dsp_no >= dsh->ds->dspcount) ++ break; ++ if (extent_contains_userdata( ++ &dsh->ds->dsp[dsp_no]->ext[ext_seq_no])) ++ found = 1; ++ } ++ if (!found) ++ return 0; ++ /* We have found the next valid extent. Get lower and upper track ++ * limits and set dsh to the first range of tracks */ ++ dsh->ext_seq_no = ext_seq_no; ++ dsh->dsp_no = dsp_no; ++ lzds_dasd_cchh2trk(dsh->ds->dsp[dsp_no]->dasdi, ++ &dsh->ds->dsp[dsp_no]->ext[ext_seq_no].llimit, ++ &dsh->extstarttrk); ++ lzds_dasd_cchh2trk(dsh->ds->dsp[dsp_no]->dasdi, ++ &dsh->ds->dsp[dsp_no]->ext[ext_seq_no].ulimit, ++ &dsh->extendtrk); ++ dsh->bufstarttrk = dsh->extstarttrk; ++ dsh->bufendtrk = dsh->bufstarttrk + (dsh->rawbufmax / RAWTRACKSIZE) - 1; ++ dsh->bufendtrk = min(dsh->bufendtrk, dsh->extendtrk); ++ dsh->rawbufsize = (dsh->bufendtrk - dsh->bufstarttrk + 1) ++ * RAWTRACKSIZE; ++ dsh->databufoffset = dsh->databufoffset + dsh->databufsize; ++ dsh->databufsize = 0; ++ dsh->bufpos = 0; ++ dsh->frameno++; ++ return 1; ++} ++ ++/** ++ * @brief subroutine of lzds_dshandle_read ++ * ++ * As we progress in reading data from the dataset, we store ++ * track/data offsets in the dshandle for late use by the ++ * lzds_dshandle_lseek and related operations. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EPROTO The existing seek buffer data is inconsistent. ++ * - EINVAL The existing seek buffer data is inconsistent. ++ * - ERANGE We try to add more elements than the prepared buffer can hold. ++ */ ++static int dshandle_store_trackframe(struct dshandle *dsh) ++{ ++ unsigned long long index; ++ ++ /* if we have no skip or seekbuf we cannot store anything */ ++ if (!dsh->skip || !dsh->seekbuf) ++ return 0; ++ ++ /* if this is a frame we want to skip, just return 0 */ ++ if (dsh->frameno % dsh->skip) ++ return 0; ++ /* make sure we do not access elements beyond the end of the buffer */ ++ if (dsh->seek_current >= dsh->seek_count) ++ return errorlog_add_message( ++ &dsh->log, ++ NULL, ERANGE, ++ "store track frame: frame list size is inconsistent\n"); ++ ++ /* our seek code relies on the fact that element n refers to frame ++ * n * skip, so we need to make sure we that we do not leave gaps */ ++ index = dsh->frameno / dsh->skip; ++ if (index > dsh->seek_current) ++ return errorlog_add_message( ++ &dsh->log, ++ NULL, EPROTO, ++ "store track frame: frame list inconsistent\n"); ++ ++ /* if we have visited this frame before, return */ ++ if (index < dsh->seek_current) { ++ if (dsh->seekbuf[index].dsp_no != dsh->dsp_no || ++ dsh->seekbuf[index].ext_seq_no != dsh->ext_seq_no || ++ dsh->seekbuf[index].bufstarttrk != dsh->bufstarttrk || ++ dsh->seekbuf[index].databufoffset != dsh->databufoffset) ++ return errorlog_add_message( ++ &dsh->log, ++ NULL, EINVAL, ++ "store track frame: frame data inconsistent\n"); ++ else ++ return 0; ++ } ++ /* the seek_current = index case */ ++ dsh->seekbuf[index].dsp_no = dsh->dsp_no; ++ dsh->seekbuf[index].ext_seq_no = dsh->ext_seq_no; ++ dsh->seekbuf[index].bufstarttrk = dsh->bufstarttrk; ++ dsh->seekbuf[index].databufoffset = dsh->databufoffset; ++ dsh->seek_current++; ++ return 0; ++} ++ ++/** ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] buf The target buffer for the read data. ++ * @param[in] size The number of bytes that are to be read. ++ * @param[out] rcsize Reference to a variable in which the actual number ++ * of read bytes is returned. ++ * If this is 0, the end of the file is reached. ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL The data in dsh is inconsistent. ++ * - ERANGE The data in dsh is inconsistent. ++ * - EPROTO The data read from the disk does not conform to the ++ * expected format. ++ * - EIO I/O error when reading from device. ++ */ ++int lzds_dshandle_read(struct dshandle *dsh, char *buf, ++ size_t size, ssize_t *rcsize) ++{ ++ ssize_t copysize; ++ int rc; ++ ++ errorlog_clear(dsh->log); ++ if (!dsh->is_open) ++ return errorlog_add_message( ++ &dsh->log, NULL, EINVAL, ++ "data set read: dshandle is not open\n"); ++ *rcsize = 0; ++ while (*rcsize < (long long)size) { ++ if (dsh->bufpos >= dsh->databufsize) { ++ /* need to fill dsh data buffer */ ++ if (dsh->eof_reached) ++ break; /* end of data in data set reached */ ++ if (!dshandle_prepare_for_next_read_tracks(dsh)) ++ break; /* end of data set extents reached */ ++ rc = lzds_dasdhandle_read_tracks_to_buffer( ++ dsh->dasdhandle[dsh->dsp_no], dsh->bufstarttrk, ++ dsh->bufendtrk, dsh->rawbuffer); ++ if (rc) ++ return errorlog_add_message( ++ &dsh->log, ++ dsh->dasdhandle[dsh->dsp_no]->log, rc, ++ "data set read: error reading data set" ++ " %s\n", dsh->ds->name); ++ rc = dshandle_extract_data_from_trackbuffer(dsh); ++ if (rc) ++ return errorlog_add_message( ++ &dsh->log, ++ dsh->log, ++ rc, ++ "data set read: extracting data set " ++ "%s from %s, tracks %u to %u\n", ++ dsh->ds->name, ++ dsh->dasdhandle[dsh->dsp_no]->dasd->device, ++ dsh->bufstarttrk, ++ dsh->bufendtrk); ++ rc = dshandle_store_trackframe(dsh); ++ if (rc) ++ return errorlog_add_message( ++ &dsh->log, ++ dsh->log, rc, ++ "data set read: storing track frame " ++ "%s\n", dsh->ds->name); ++ } ++ /* if databuf has data to copy */ ++ if (dsh->bufpos < dsh->databufsize) { ++ /* copy data from databuf to buf */ ++ copysize = min(((long long)size - *rcsize), ++ (dsh->databufsize - dsh->bufpos)); ++ memcpy(buf, &dsh->databuffer[dsh->bufpos], copysize); ++ buf += copysize; ++ dsh->bufpos += copysize; ++ *rcsize += copysize; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * @brief subroutine of lzds_dshandle_lseek ++ * ++ * Find the closest buffered seekelement that starts before offset ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] offset The data offset in the dataset that we want to reach. ++ * @param[out] se_index Reference to a variable in which the found index ++ * to dsh->seekbuf is returned. ++ * ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL There is no seekbuffer available. ++ */ ++static int dshandle_find_seekelement(struct dshandle *dsh, off_t offset, ++ long long *se_index) ++{ ++ unsigned long long low, high, index; ++ ++ if (!dsh->seek_current) ++ return EINVAL; ++ ++ /* special case for the last element in the list */ ++ if (dsh->seekbuf[dsh->seek_current - 1].databufoffset <= offset) { ++ *se_index = dsh->seek_current - 1; ++ return 0; ++ } ++ /* search starts with 'high' set to the second to last element */ ++ index = 0; ++ high = dsh->seek_current - 2; ++ low = 0; ++ *se_index = 0; ++ index = (high + low) / 2; ++ while (low != high) { ++ if (dsh->seekbuf[index].databufoffset <= offset) { ++ low = index; ++ index = (high + low + 1) / 2; ++ } else { ++ high = index - 1; ++ index = (high + low) / 2; ++ } ++ } ++ *se_index = low; ++ return 0; ++} ++ ++/** ++ * @brief subroutine of lzds_dshandle_lseek ++ * ++ * Reset the internel buffers etc, so that the next read will read ++ * the track frame pointed to by the seekelement. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[out] se_index Index to the seekelement in dsh->seekbuf. ++ */ ++static void dshandle_reset_buffer_position_to_seekelement( ++ struct dshandle *dsh, long long se_index) ++{ ++ /* make sure that read knows that we have no ready data in our buffer */ ++ dsh->bufpos = 0; ++ dsh->databufsize = 0; ++ dsh->eof_reached = 0; ++ ++ /* we need to set the bufendtrk and sequence number so, ++ * that the current track buffer seems to end with the ++ * track that comes before the first track of the ++ * data set or member ++ */ ++ ++ /* framno will be incremented during read, so do a -1 here */ ++ dsh->frameno = (se_index * dsh->skip) - 1; ++ dsh->databufoffset = dsh->seekbuf[se_index].databufoffset; ++ dsh->dsp_no = dsh->seekbuf[se_index].dsp_no; ++ ++ /* For a partitioned data set we need to find the correct start ++ * track and point the current buffer just before it. ++ * As we always need to read full tracks, any additional ++ * record offset will be set explicitly and handled during ++ * track interpretation. ++ */ ++ if (dsh->member && (dsh->frameno == -1)) ++ dsh->startrecord = dsh->member->record; ++ ++ /* In most cases our track frame will be in the middle of the ++ * disk, so we set bufendtrk to the last track before our track ++ * frame. In the special case that the track frame begins ++ * on track 0, we set the ext_seq_no to that of the frame -1, ++ * so that the read code will advance to the next extend and ++ * the first track of that extent ++ */ ++ if (!dsh->seekbuf[se_index].bufstarttrk) { ++ dsh->ext_seq_no = dsh->seekbuf[se_index].ext_seq_no - 1; ++ dsh->bufendtrk = 0; ++ dsh->extendtrk = 0; ++ return; ++ } ++ ++ dsh->ext_seq_no = dsh->seekbuf[se_index].ext_seq_no; ++ ++ lzds_dasd_cchh2trk(dsh->ds->dsp[dsh->dsp_no]->dasdi, ++ &dsh->ds->dsp[dsh->dsp_no]->ext[dsh->ext_seq_no].llimit, ++ &dsh->extstarttrk); ++ lzds_dasd_cchh2trk(dsh->ds->dsp[dsh->dsp_no]->dasdi, ++ &dsh->ds->dsp[dsh->dsp_no]->ext[dsh->ext_seq_no].ulimit, ++ &dsh->extendtrk); ++ ++ dsh->bufstarttrk = 0; ++ dsh->bufendtrk = dsh->seekbuf[se_index].bufstarttrk - 1; ++ ++ dsh->rawbufsize = 0; ++ dsh->databufoffset = dsh->seekbuf[se_index].databufoffset; ++ dsh->databufsize = 0; ++ dsh->bufpos = 0; ++ return; ++} ++ ++/** ++ * It is not possible to seek beyond the end of the data, but an ++ * attempt to do so is a common occurrence as we may not know the ++ * actual data size beforehand. In this case, the returned rcoffset ++ * is smaller than offset and points to the offset directly following ++ * the last data byte. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] offset The data offset in the dataset that we want to reach. ++ * @param[out] rcoffset Reference to a variable in which the actual offset ++ * is returned. ++ * ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EINVAL The data in dsh is inconsistent. ++ * - ERANGE The data in dsh is inconsistent. ++ * - EPROTO The data read from the disk does not conform to the ++ * expected format. ++ * - EIO I/O error when reading from device. ++ */ ++int lzds_dshandle_lseek(struct dshandle *dsh, long long offset, ++ long long *rcoffset) ++{ ++ char foo; ++ ssize_t rcsize = 0; ++ int rc; ++ long long se_index; ++ ++ errorlog_clear(dsh->log); ++ if (dsh->databufoffset <= offset && ++ offset < dsh->databufoffset + dsh->databufsize) { ++ /* offset is within the current track frame */ ++ dsh->bufpos = offset - dsh->databufoffset; ++ *rcoffset = offset; ++ return 0; ++ } ++ /* need to seek to some other track frame */ ++ if (!dshandle_find_seekelement(dsh, offset, &se_index)) { ++ /* do not reset our context if we can seek forward from ++ * our current position */ ++ if (!(dsh->seekbuf[se_index].databufoffset < dsh->databufoffset ++ && dsh->databufoffset <= offset)) { ++ dshandle_reset_buffer_position_to_seekelement(dsh, ++ se_index); ++ } ++ } else if (offset < dsh->databufoffset) { ++ /* if we have no seekbuffer, we can only reset to the ++ * start of the data set */ ++ rc = initialize_buffer_positions_for_first_read(dsh); ++ if (rc) ++ return errorlog_add_message( ++ &dsh->log, ++ dsh->log, rc, ++ "data set seek: error when initializing buffers" ++ " for data set %s\n", dsh->ds->name); ++ } ++ /* from here on we just need to go forward by reading track ++ * frames until we find a frame that contains the offset ++ */ ++ while (dsh->databufoffset + dsh->databufsize <= offset) { ++ dsh->bufpos = dsh->databufsize; ++ rc = lzds_dshandle_read(dsh, &foo, sizeof(foo), &rcsize); ++ if (rc || !rcsize) { ++ *rcoffset = dsh->databufoffset + dsh->databufsize; ++ if (rc) ++ errorlog_add_message( ++ &dsh->log, ++ dsh->log, rc, ++ "data set seek: error reading data from" ++ " data set %s\n", dsh->ds->name); ++ return rc; ++ } ++ } ++ dsh->bufpos = offset - dsh->databufoffset; ++ *rcoffset = offset; ++ return 0; ++} ++ ++/** ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[out] offset Reference to a variable in which the current offset ++ * is returned. ++ */ ++void lzds_dshandle_get_offset(struct dshandle *dsh, long long *offset) ++{ ++ *offset = dsh->databufoffset + dsh->bufpos; ++} ++ ++/** ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[out] log Reference to a variable in which the errorlog ++ * is returned. ++ */ ++void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log) ++{ ++ *log = dsh->log; ++} ++ ++ ++/******************************************************************************/ ++/* libzds helper functions */ ++/******************************************************************************/ ++ ++/** ++ * This function takes the DS1RECFM byte as defined for the format 1 DSCB, and ++ * creates a string of the usual characters F, V, U, T, B, S, A, and M. ++ * ++ * @param[in] DS1RECFM Input byte. ++ * @param[out] buffer Buffer for the output string. ++ * The buffer must be at least 7 characters long. ++ */ ++void lzds_DS1RECFM_to_recfm(char DS1RECFM, char *buffer) ++{ ++ if ((DS1RECFM & 0x80) && !(DS1RECFM & 0x40)) ++ *buffer++ = 'F'; /* fixed records */ ++ ++ if (!(DS1RECFM & 0x80) && (DS1RECFM & 0x40)) ++ *buffer++ = 'V'; /* variable records */ ++ ++ if ((DS1RECFM & 0x80) && (DS1RECFM & 0x40)) ++ *buffer++ = 'U'; /* undefined length records */ ++ ++ if ((DS1RECFM & 0x20)) ++ *buffer++ = 'T'; /* track overflow (legacy) */ ++ ++ if ((DS1RECFM & 0x10)) ++ *buffer++ = 'B'; /* blocked */ ++ ++ if ((DS1RECFM & 0x08)) ++ *buffer++ = 'S'; /* standard records */ ++ ++ if ((DS1RECFM & 0x04) && !(DS1RECFM & 0x02)) ++ *buffer++ = 'A'; /* ISO / ANSI control characters */ ++ ++ if (!(DS1RECFM & 0x04) && (DS1RECFM & 0x02)) ++ *buffer++ = 'M'; /* machine control characters */ ++ *buffer = 0; ++ ++ /* The combinations ((DS1RECFM & 0x04) && (DS1RECFM & 0x02)) ++ * and (DS1RECFM & 0x01) are reserved ++ * ++ * If we count only one byte for the mutual exclusive F, V and U, ++ * three bytes for T, B and S, ++ * one byte for the mutual exclusive A and M, ++ * one byte for a possible future definition of 0x01, and ++ * one byte for the zero termination, ++ * then we get a required buffer length of 7 bytes ++ */ ++} ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/zdsfs/Makefile b/zdsfs/Makefile +new file mode 100644 +index 0000000..ecbc124 +--- /dev/null ++++ b/zdsfs/Makefile +@@ -0,0 +1,22 @@ ++include ../common.mak ++ ++CPPFLAGS += -I../include -DSYSFS ++CFLAGS += -D_FILE_OFFSET_BITS=64 -DHAVE_SETXATTR -I/usr/include/fuse -pthread ++LDLIBS += -lfuse -lpthread -lrt -ldl -lm -lzds ++LDFLAGS += -L../libzds ++ ++all: zdsfs ++ ++zdsfs: zdsfs.o ++ ++zdsfs.o: ../include/zt_common.h ../include/libzds.h ++ ++install: all ++ $(INSTALL) -d -m 755 $(USRBINDIR) $(MANDIR)/man1 ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zdsfs $(USRBINDIR) ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 zdsfs.1 $(MANDIR)/man1 ++ ++clean: ++ rm -f *.o *~ zdsfs core ++ ++.PHONY: all install clean +diff --git a/zdsfs/zdsfs.1 b/zdsfs/zdsfs.1 +new file mode 100644 +index 0000000..4f8cc97 +--- /dev/null ++++ b/zdsfs/zdsfs.1 +@@ -0,0 +1,270 @@ ++.\" IBM Corporation Copyright 2013 ++.\" ++.TH ZDSFS 1 "2013" "s390-tools" ++ ++.SH NAME ++zdsfs \- File system for z/OS data set access ++ ++.SH SYNOPSIS ++.SS mounting: ++.TP ++\fBzdsfs\fP \fI\fR \fI\fR [\fI\fR] ++.SS unmounting: ++.TP ++\fBfusermount\fP -u \fI\fR ++ ++.SH DESCRIPTION ++ ++Use the \fBzdsfs\fP command for read access to z/OS data sets ++stored on one or more DASDs. ++ ++The zdsfs file system translates the record-based z/OS data sets to ++UNIX file system semantics. After mounting the devices, you can use ++common Linux tools to access the files on the disk. Physical ++sequential data sets are represented as files. Partitioned data sets ++are represented as directories, with each member being represented as ++a file in that directory. ++ ++.SH RESTRICTIONS ++Only read access is supported. ++ ++Data sets on tape devices are not supported. ++ ++To maintain data consistency, a DASD must not be modified while it is ++in use by zdsfs. This can be assured by varying the device offline ++in z/OS before setting it online in Linux. ++ ++The access to the device by Linux is not subject to RACF or any other ++z/OS auditing mechanism. The safety of the data on the device must be ++established by the respective Linux mechanisms. The default behavior ++of zdsfs is to grant access to the files in the fuse file system only ++to the user who has started the tool. This behavior can be ++configured by using the options `allow_other', `default_permissions', ++`umask', `uid', and `gid'. ++ ++Only physical sequential (PS) and partitioned data sets (PDS) are ++supported. Supported record formats are: V, F, U, B, S, A, and M. ++ ++The file system is limited to basic operations (readdir, stat, open, ++read, seek). Because the actual size of the data in each track is not ++always known, zdsfs does not support mmap. Seek operations read the ++whole data set to the given offset. The performance of seek ++operations to previous offsets can be improved by buffering seek ++offsets, see option `-o seekbuffers'. ++ ++A further consequence of the unknown exact data size is that zdsfs ++cannot provide exact file sizes. As a heuristic, the given file sizes ++are the maximum possible data sizes, based on the number and size of ++the extents that belong to each data set. When the actual end of the ++data is reached during read, the usual end of file (EOF) is returned. ++To make sure that the EOF is passed to the user correctly, the option ++`-o direct_io' is set by zdsfs implicitly. ++ ++The detection of incomplete multi volume data sets does not work for ++data sets for which only the first volume (device) is present. ++ ++.SH OPTIONS ++.SS "general options:" ++ ++.TP ++\fB\fR One or more DASD device nodes, where node specifications are ++separated by blanks. The device nodes can be specified explicitly with ++the command or with the -l option and a file. ++.TP ++\fB\fR The mount point for the specified DASD. ++.TP ++\fB\-o\fR \fI\fR,[\fI\fR...] Fuse or mount command ++options. For fuse options see "Applicable FUSE options" below, for ++mount options see \fBmount(8)\fP. ++.TP ++\fB\-h\fR or \fB\-\-help\fR ++Print usage information, then exit. ++.TP ++\fB\-v\fR or \fB\-\-version\fR ++Print version information, then exit. ++.SS "zdsfs options:" ++.TP ++\fB\-l\fR \fI\fR ++The specified file \fI\fR contains a list of device ++nodes, separated by white space (space, tab or new line). All device ++nodes in this file are mounted as if given directly via the command ++line. ++.TP ++\fB\-o\fR rdw ++Keep record descriptor words in the byte stream. By default, data set ++files contain only the user data. ++ ++Record boundaries might be important for applications to correctly ++interpret the user data. For data sets with variable records, the ++record descriptor words are required to find the record ++boundaries. With fixed blocks, record boundaries can be computed from ++the fixed record sizes. ++ ++See `z/OS DFSMS Using Data Sets' for more information about record ++descriptor words. ++.TP ++\fB\-o\fR ignore_incomplete ++Continue processing even if parts of a multi-volume data set are ++missing. By default, zdsfs ends with an error unless all data sets ++are complete. ++ ++Incomplete data sets can be tolerated, for example, if all data of ++interest is on another data set that is complete. ++Incomplete data sets are not represented in the file system. Instead, ++for each incomplete data set, a warning message is written to the ++standard error stream. ++.TP ++\fB\-o\fR tracks=\fI\fR ++Size of the track buffer in tracks. The default for \fI\fR is 128. ++ ++The data that is read from the DASD has to be stored in a buffer, because ++the minimum size for a read operation in raw access mode is one track, ++and the user data has to be extracted from the track images. Reading ++more than one track at a time improves the overall performance, but ++requires larger buffers. ++ ++The memory needed by zdsfs for buffering a single track is 64KB for the ++raw track data and 56KB for the extracted user data. Each time a file ++is opened a total of (\fI\fR * 120KB) is allocated for the track buffer. ++ ++.TP ++\fB\-o\fR seekbuffer=\fI\fR ++Upper limit in bytes for the seek history buffer size. The default for ++\fIs\fR is 1048576. ++ ++Because the block and record sizes in a data set may vary, ++the only way to find a data byte at a particular offset (`seek') is ++to read and interpret the whole data set from the beginning, until ++the offset is reached. ++ ++To improve the performance of `seek' operations in areas that have ++already been read, zdsfs can buffer offsets in regular ++intervals. These intervals are multiples of \fI\fR tracks, as specified ++with the `tracks' option. ++ ++For small data sets and large values of \fI\fR, only a few seek offsets ++need to be buffered. In this case, the amount of memory that is ++actually allocated can be much smaller than the upper limit \fI\fR. ++ ++If \fI\fR is set to 0, no seek history buffer is allocated. In this ++case `seek' is still supported, but a `seek' operation might result in a ++read from the beginning of the data set. ++ ++.SS "Applicable FUSE options (version 2.8):" ++This is a selected subset of all FUSE options. Use the zdsfs ++\fB\--help\fR option to print a full list. ++ ++.TP ++\fB\-d\fR or \fB\-o\fR debug ++Enable debug output (implies \fB\-f\fR) ++.TP ++\fB\-f\fR ++Foreground operation ++.TP ++\fB\-o\fR allow_other ++Allow access by other users ++.TP ++\fB\-o\fR allow_root ++Allow access by root ++.TP ++\fB\-o\fR nonempty ++Allow mounts over non\-empty file/dir ++.TP ++\fB\-o\fR default_permissions ++Enable permission checking by kernel ++.TP ++\fB\-o\fR max_read=\fI\fR ++Set maximum size of read requests ++.TP ++\fB\-o\fR kernel_cache ++Cache files in kernel ++.TP ++\fB\-o\fR [no]auto_cache ++Enable caching based on modification times ++.TP ++\fB\-o\fR umask=\fI\fR ++Set file permissions (octal) ++.TP ++\fB\-o\fR uid=\fI\fR ++Set file owner ++.TP ++\fB\-o\fR gid=\fI\fR ++Set file group ++.TP ++\fB\-o\fR max_readahead=\fI\fR ++Set maximum readahead ++.TP ++\fB\-o\fR async_read ++Perform reads asynchronously (default) ++.TP ++\fB\-o\fR sync_read ++Perform reads synchronously ++ ++ ++.SH DATA SET CHARACTERISTICS ++ ++Certain data set characteristics might be required for the correct ++interpretation of the data. The collected metadata of all data sets ++can be found in a file `metadata.txt' in the top directory of the ++mounted file system. ++ ++This file contains one line per data set, with the syntax that is used ++by z/OS. ++ ++dsn=,recfm=,lrecl=,dsorg= ++ ++\fBdsn\fR: The data set name. ++For physical sequential data sets this is the ++same name as the file name in the mount directory. For partitioned ++data sets (PDS) this is the same as the directory name in the mount ++directory. For PDS members the member name is placed in parentheses ++after the PDS name. ++ ++\fBrecfm\fR: The record format. ++ ++\fBlrecl\fR: The logical record length. ++ ++\fBdsorg\fR: The data set organization. ++For partitioned data sets the organization is `PO', but for ++partitioned data set members it is `PS'. ++ ++In addition to the `metadata.txt' file, you can use the following ++extended attributes to read the data set characteristics of a file or ++directory: ++ ++\fBuser.recfm\fR: The record format. ++ ++\fBuser.lrecl\fR: The logical record length. ++ ++\fBuser.dsorg\fR: The data set organization of a file. ++ ++ ++.SH EXAMPLES ++To mount the z/OS disk with the name dasde enter: ++.br ++ ++ # zdsfs /dev/dasde /mnt ++ ++.br ++ ++To mount the z/OS disk with space for 4 tracks and keeping the record ++descriptor words in the byte stream, enter: ++.br ++ ++ # zdsfs -o rdw -o tracks=4 /dev/dasde /mnt ++ ++.br ++ ++To unmount the z/OS disk mounted on /mnt enter: ++.br ++ ++ # fusermount -u /mnt ++ ++To list all extended attributes of file FOOBAR.TEST.TXT ++assuming the z/OS disk was mounted on /mnt: ++ ++ # getfattr -d /mnt/FOOBAR.TEST.TXT ++ ++.SH SEE ALSO ++getfattr(1), fuse(8), z/OS DFSMS Using Data Sets, ++and Linux on System z: Device Drivers, Features and Commands +diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c +new file mode 100644 +index 0000000..0fe921a +--- /dev/null ++++ b/zdsfs/zdsfs.c +@@ -0,0 +1,1032 @@ ++/* ++ * FUSE file system for z/OS data set access ++ * Copyright IBM Corporation 2013 ++ */ ++ ++/* The fuse version define tells fuse that we want to use the new API */ ++#define FUSE_USE_VERSION 26 ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_SETXATTR ++#include ++#endif ++ ++ ++#include "zt_common.h" ++ ++#include "libzds.h" ++ ++#define COMP "zdsfs: " ++#define METADATAFILE "metadata.txt" ++ ++/* defaults for file and directory permissions (octal) */ ++#define DEF_FILE_PERM 0440 ++#define DEF_DIR_PERM 0550 ++ ++struct zdsfs_info { ++ int devcount; ++ int allow_inclomplete_multi_volume; ++ int keepRDW; ++ unsigned int tracks_per_frame; ++ unsigned long long seek_buffer_size; ++ struct zdsroot *zdsroot; ++ ++ char *metadata; /* buffer that contains the content of metadata.txt */ ++ size_t metasize; /* total size of meta data buffer */ ++ size_t metaused; /* how many bytes of buffer are already filled */ ++ time_t metatime; /* when did we create this meta data */ ++}; ++ ++static struct zdsfs_info zdsfsinfo; ++ ++struct zdsfs_file_info { ++ struct dshandle *dsh; ++ pthread_mutex_t mutex; ++ ++ int is_metadata_file; ++ size_t metaread; /* how many bytes have already been read */ ++}; ++ ++ ++ ++/* normalize the given path name to a dataset name ++ * so that we can compare it to the names in the vtoc. This means: ++ * - remove the leading / ++ * - remove member names (everything after a subsequent / ) ++ * Note: we do no upper case, EBCDIC conversion or padding ++ */ ++static void path_to_ds_name(const char *path, char *normds, size_t size) ++{ ++ char *end; ++ ++ if (*path == '/') ++ ++path; ++ strncpy(normds, path, size); ++ normds[size - 1] = 0; ++ end = strchr(normds, '/'); ++ if (end) ++ *end = 0; ++} ++ ++static void path_to_member_name(const char *path, char *normds, size_t size) ++{ ++ if (*path == '/') ++ ++path; ++ path = strchr(path, '/'); ++ if (!path) ++ normds[0] = 0; ++ else { ++ ++path; ++ strncpy(normds, path, size); ++ normds[size - 1] = 0; ++ } ++} ++ ++ ++ ++static int zdsfs_getattr(const char *path, struct stat *stbuf) ++{ ++ char normds[MAXDSNAMELENGTH]; ++ size_t dssize; ++ struct dataset *ds; ++ struct pdsmember *member; ++ int rc, ispds, issupported; ++ unsigned long long tracks; ++ format1_label_t *f1; ++ time_t time; ++ struct tm tm; ++ ++ memset(stbuf, 0, sizeof(struct stat)); ++ /* root directory case */ ++ if (strcmp(path, "/") == 0) { ++ stbuf->st_mode = S_IFDIR | DEF_DIR_PERM; ++ stbuf->st_nlink = 2; ++ stbuf->st_atime = zdsfsinfo.metatime; ++ stbuf->st_mtime = zdsfsinfo.metatime; ++ stbuf->st_ctime = zdsfsinfo.metatime; ++ return 0; ++ } ++ ++ if (strcmp(path, "/"METADATAFILE) == 0) { ++ stbuf->st_mode = S_IFREG | DEF_FILE_PERM; ++ stbuf->st_nlink = 1; ++ stbuf->st_size = zdsfsinfo.metaused; ++ stbuf->st_atime = zdsfsinfo.metatime; ++ stbuf->st_mtime = zdsfsinfo.metatime; ++ stbuf->st_ctime = zdsfsinfo.metatime; ++ return 0; ++ } ++ ++ path_to_ds_name(path, normds, sizeof(normds)); ++ rc = lzds_zdsroot_find_dataset(zdsfsinfo.zdsroot, normds, &ds); ++ if (rc) ++ return -rc; ++ ++ lzds_dataset_get_is_supported(ds, &issupported); ++ if (!issupported) ++ return -ENOENT; ++ ++ /* upper limit for the size of the whole data set */ ++ lzds_dataset_get_size_in_tracks(ds, &tracks); ++ dssize = MAXRECSIZE * tracks; ++ ++ /* get the last access time */ ++ lzds_dataset_get_format1_dscb(ds, &f1); ++ memset(&tm, 0, sizeof(tm)); ++ if (f1->DS1REFD.year || f1->DS1REFD.day) { ++ tm.tm_year = f1->DS1REFD.year; ++ tm.tm_mday = f1->DS1REFD.day; ++ } else { ++ tm.tm_year = f1->DS1CREDT.year; ++ tm.tm_mday = f1->DS1CREDT.day; ++ } ++ tm.tm_isdst = -1; ++ time = mktime(&tm); ++ if (time < 0) ++ time = 0; ++ ++ lzds_dataset_get_is_PDS(ds, &ispds); ++ if (ispds) { ++ path_to_member_name(path, normds, sizeof(normds)); ++ /* the dataset itself is represented as directory */ ++ if (strcmp(normds, "") == 0) { ++ stbuf->st_mode = S_IFDIR | DEF_DIR_PERM; ++ stbuf->st_nlink = 2; ++ stbuf->st_size = 0; ++ stbuf->st_atime = time; ++ stbuf->st_mtime = time; ++ stbuf->st_ctime = time; ++ stbuf->st_blocks = tracks * 16 * 8; ++ stbuf->st_size = dssize; ++ return 0; ++ } ++ rc = lzds_dataset_get_member_by_name(ds, normds, &member); ++ if (rc) ++ return -ENOENT; ++ stbuf->st_mode = S_IFREG | DEF_FILE_PERM; ++ stbuf->st_nlink = 1; ++ /* the member cannot be bigger than the data set */ ++ stbuf->st_size = dssize; ++ return 0; ++ } else { /* normal data set */ ++ stbuf->st_mode = S_IFREG | DEF_FILE_PERM; ++ stbuf->st_nlink = 1; ++ stbuf->st_size = dssize; ++ stbuf->st_blocks = tracks * 16 * 8; ++ stbuf->st_atime = time; ++ stbuf->st_mtime = time; ++ stbuf->st_ctime = time; ++ } ++ return 0; ++} ++ ++static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs) ++{ ++ struct dasditerator *dasdit; ++ unsigned int cyls, heads; ++ struct dasd *dasd; ++ struct dataset *ds; ++ struct dsiterator *dsit; ++ unsigned long long totaltracks, usedtracks, dstracks; ++ int rc; ++ ++ totaltracks = 0; ++ rc = lzds_zdsroot_alloc_dasditerator(zdsfsinfo.zdsroot, &dasdit); ++ if (rc) ++ return -ENOMEM; ++ while (!lzds_dasditerator_get_next_dasd(dasdit, &dasd)) { ++ lzds_dasd_get_cylinders(dasd, &cyls); ++ lzds_dasd_get_heads(dasd, &heads); ++ totaltracks += cyls * heads; ++ } ++ lzds_dasditerator_free(dasdit); ++ ++ usedtracks = 0; ++ rc = lzds_zdsroot_alloc_dsiterator(zdsfsinfo.zdsroot, &dsit); ++ if (rc) ++ return -ENOMEM; ++ while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) { ++ /* To compute the occupied space we consider all data sets, ++ * not just the supported ones */ ++ lzds_dataset_get_size_in_tracks(ds, &dstracks); ++ usedtracks += dstracks; ++ } ++ lzds_dsiterator_free(dsit); ++ ++ if (totaltracks < usedtracks) ++ return -EPROTO; ++ ++ memset(statvfs, 0, sizeof(*statvfs)); ++ statvfs->f_bsize = RAWTRACKSIZE; ++ statvfs->f_frsize = RAWTRACKSIZE; ++ statvfs->f_blocks = totaltracks; ++ statvfs->f_bfree = totaltracks - usedtracks; ++ statvfs->f_bavail = totaltracks - usedtracks; ++ statvfs->f_namemax = MAXDSNAMELENGTH - 1; ++ 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)) ++{ ++ char normds[MAXDSNAMELENGTH]; ++ char *mbrname; ++ char *dsname; ++ struct dataset *ds; ++ struct dsiterator *dsit; ++ struct memberiterator *it; ++ struct pdsmember *member; ++ int rc; ++ int ispds, issupported; ++ ++ /* we have two type of directories ++ * type one: the root directory contains all data sets ++ */ ++ if (strcmp(path, "/") == 0) { ++ filler(buf, ".", NULL, 0); ++ filler(buf, "..", NULL, 0); ++ filler(buf, METADATAFILE, NULL, 0); ++ /* note that we do not need to distinguish between ++ * normal files and directories here, that is done ++ * in the rdf_getattr function ++ */ ++ rc = lzds_zdsroot_alloc_dsiterator(zdsfsinfo.zdsroot, &dsit); ++ if (rc) ++ return -ENOMEM; ++ while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) { ++ lzds_dataset_get_is_supported(ds, &issupported); ++ if (issupported) { ++ lzds_dataset_get_name(ds, &dsname); ++ filler(buf, dsname, NULL, 0); ++ } ++ } ++ lzds_dsiterator_free(dsit); ++ return 0; ++ } ++ ++ /* type two: a partitioned data set, contains all PDS members */ ++ path_to_ds_name(path, normds, sizeof(normds)); ++ rc = lzds_zdsroot_find_dataset(zdsfsinfo.zdsroot, normds, &ds); ++ if (rc) ++ return -ENOENT; ++ lzds_dataset_get_is_PDS(ds, &ispds); ++ if (ispds) { ++ filler(buf, ".", NULL, 0); ++ filler(buf, "..", NULL, 0); ++ rc = lzds_dataset_alloc_memberiterator(ds, &it); ++ if (rc) ++ return -ENOMEM; ++ while (!lzds_memberiterator_get_next_member(it, &member)) { ++ lzds_pdsmember_get_name(member, &mbrname); ++ filler(buf, mbrname, NULL, 0); ++ } ++ lzds_memberiterator_free(it); ++ } else ++ return -ENOENT; ++ ++ return 0; ++} ++ ++ ++static int zdsfs_open(const char *path, struct fuse_file_info *fi) ++{ ++ char normds[45]; ++ struct dshandle *dsh; ++ struct dataset *ds; ++ struct zdsfs_file_info *zfi; ++ int rc; ++ int ispds, issupported; ++ struct errorlog *log; ++ ++ if ((fi->flags & 3) != O_RDONLY) ++ return -EACCES; ++ /* ++ * The contents of the dataset is smaller than the data set ++ * size we return in zdsfs_getattr. We need to set direct_io ++ * to make sure that fuse will report the end of the data with EOF. ++ */ ++ fi->direct_io = 1; ++ ++ zfi = malloc(sizeof(*zfi)); ++ if (!zfi) ++ return -ENOMEM; ++ rc = pthread_mutex_init(&zfi->mutex, NULL); ++ if (rc) ++ goto error1; ++ ++ if (strcmp(path, "/"METADATAFILE) == 0) { ++ zfi->dsh = NULL; ++ zfi->is_metadata_file = 1; ++ zfi->metaread = 0; ++ fi->fh = (unsigned long)zfi; ++ return 0; ++ } ++ ++ path_to_ds_name(path, normds, sizeof(normds)); ++ rc = lzds_zdsroot_find_dataset(zdsfsinfo.zdsroot, normds, &ds); ++ if (rc) ++ goto error1; ++ ++ lzds_dataset_get_is_supported(ds, &issupported); ++ if (!issupported) { ++ /* we should never get this error, as unsupported data sets are ++ * not listed. But just in case, print a message */ ++ fprintf(stderr, "Error: Data set %s is not supported\n", normds); ++ rc = -ENOENT; ++ goto error1; ++ } ++ ++ rc = lzds_dataset_alloc_dshandle(ds, zdsfsinfo.tracks_per_frame, &dsh); ++ if (rc) { ++ rc = -rc; ++ goto error1; ++ } ++ ++ rc = lzds_dshandle_set_seekbuffer(dsh, zdsfsinfo.seek_buffer_size); ++ if (rc) { ++ fprintf(stderr, "Error when preparing seek buffer:\n"); ++ lzds_dshandle_get_errorlog(dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ rc = -rc; ++ goto error2; ++ } ++ /* if the data set is a PDS, then the path must contain a valid ++ * member name, and the context must be set to this member ++ */ ++ lzds_dataset_get_is_PDS(ds, &ispds); ++ if (ispds) { ++ path_to_member_name(path, normds, sizeof(normds)); ++ rc = lzds_dshandle_set_member(dsh, normds); ++ if (rc) { ++ fprintf(stderr, "Error when preparing member:\n"); ++ lzds_dshandle_get_errorlog(dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ rc = -rc; ++ goto error2; ++ } ++ } ++ rc = lzds_dshandle_set_keepRDW(dsh, zdsfsinfo.keepRDW); ++ if (rc) { ++ fprintf(stderr, "Error when preparing RDW setting:\n"); ++ lzds_dshandle_get_errorlog(dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ rc = -rc; ++ goto error2; ++ } ++ rc = lzds_dshandle_open(dsh); ++ if (rc) { ++ fprintf(stderr, "Error when opening data set:\n"); ++ lzds_dshandle_get_errorlog(dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ rc = -rc; ++ goto error2; ++ } ++ zfi->is_metadata_file = 0; ++ zfi->metaread = 0; ++ zfi->dsh = dsh; ++ fi->fh = (uint64_t)(unsigned long)zfi; ++ return 0; ++ ++error2: ++ lzds_dshandle_free(dsh); ++error1: ++ free(zfi); ++ return rc; ++ ++} ++ ++static int zdsfs_release(const char *UNUSED(path), struct fuse_file_info *fi) ++{ ++ struct zdsfs_file_info *zfi; ++ int rc; ++ ++ if (!fi->fh) ++ return -EINVAL; ++ zfi = (struct zdsfs_file_info *)(unsigned long)fi->fh; ++ if (zfi->dsh) { ++ lzds_dshandle_close(zfi->dsh); ++ lzds_dshandle_free(zfi->dsh); ++ } ++ rc = pthread_mutex_destroy(&zfi->mutex); ++ if (rc) ++ fprintf(stderr, "Error: could not destroy mutex, rc=%d\n", rc); ++ free(zfi); ++ return 0; ++} ++ ++static int zdsfs_read(const char *UNUSED(path), char *buf, size_t size, ++ off_t offset, struct fuse_file_info *fi) ++{ ++ struct zdsfs_file_info *zfi; ++ ssize_t count; ++ int rc, rc2; ++ long long rcoffset; ++ struct errorlog *log; ++ ++ if (!fi->fh) ++ return -ENOENT; ++ zfi = (struct zdsfs_file_info *)(unsigned long)fi->fh; ++ ++ rc2 = pthread_mutex_lock(&zfi->mutex); ++ if (rc2) { ++ fprintf(stderr, "Error: could not lock mutex, rc=%d\n", rc2); ++ return -EIO; ++ } ++ rc = 0; ++ if (zfi->is_metadata_file) { ++ if (zfi->metaread >= zdsfsinfo.metaused) { ++ pthread_mutex_unlock(&zfi->mutex); ++ return 0; ++ } ++ count = zdsfsinfo.metaused - zfi->metaread; ++ if (size < (size_t)count) ++ count = size; ++ memcpy(buf, &zdsfsinfo.metadata[zfi->metaread], count); ++ zfi->metaread += count; ++ } else { ++ lzds_dshandle_get_offset(zfi->dsh, &rcoffset); ++ if (rcoffset != offset) ++ rc = lzds_dshandle_lseek(zfi->dsh, offset, &rcoffset); ++ if (!rc) ++ rc = lzds_dshandle_read(zfi->dsh, buf, size, &count); ++ } ++ rc2 = pthread_mutex_unlock(&zfi->mutex); ++ if (rc2) ++ fprintf(stderr, "Error: could not unlock mutex, rc=%d\n", rc2); ++ if (rc) { ++ fprintf(stderr, "Error when reading from data set:\n"); ++ lzds_dshandle_get_errorlog(zfi->dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ return -rc; ++ } ++ return count; ++} ++ ++ ++#ifdef HAVE_SETXATTR ++ ++#define RECFMXATTR "user.recfm" ++#define LRECLXATTR "user.lrecl" ++#define DSORGXATTR "user.dsorg" ++ ++static int zdsfs_listxattr(const char *path, char *list, size_t size) ++{ ++ int pos = 0; ++ size_t list_len; ++ ++ /* root directory and the metadata have no extended attributes */ ++ if (!strcmp(path, "/") || !strcmp(path, "/"METADATAFILE)) ++ return 0; ++ ++ list_len = strlen(RECFMXATTR) + 1 + ++ strlen(LRECLXATTR) + 1 + ++ strlen(DSORGXATTR) + 1; ++ /* size 0 is a special case to query the necessary buffer length */ ++ if (!size) ++ return list_len; ++ if (size < list_len) ++ return -ERANGE; ++ strcpy(list, RECFMXATTR); ++ pos += strlen(RECFMXATTR) + 1; ++ strcpy(&list[pos], LRECLXATTR); ++ pos += strlen(LRECLXATTR) + 1; ++ strcpy(&list[pos], DSORGXATTR); ++ pos += strlen(DSORGXATTR) + 1; ++ return pos; ++} ++ ++static int zdsfs_getxattr(const char *path, const char *name, char *value, ++ size_t size) ++{ ++ char normds[45]; ++ struct dataset *ds; ++ format1_label_t *f1; ++ int rc; ++ char buffer[20]; ++ size_t length; ++ int ispds; ++ ++ /* nothing for root directory but clear error code needed */ ++ if (!strcmp(path, "/") || !strcmp(path, "/"METADATAFILE)) ++ return -ENODATA; ++ ++ path_to_ds_name(path, normds, sizeof(normds)); ++ rc = lzds_zdsroot_find_dataset(zdsfsinfo.zdsroot, normds, &ds); ++ if (rc) ++ return -rc; ++ lzds_dataset_get_format1_dscb(ds, &f1); ++ ++ /* null terminate strings */ ++ memset(value, 0, size); ++ memset(buffer, 0, sizeof(buffer)); ++ ++ /* returned size should be the string length, getfattr fails if I ++ * include an extra byte for the zero termination */ ++ if (strcmp(name, RECFMXATTR) == 0) { ++ lzds_DS1RECFM_to_recfm(f1->DS1RECFM, buffer); ++ } else if (strcmp(name, LRECLXATTR) == 0) { ++ snprintf(buffer, sizeof(buffer), "%d", f1->DS1LRECL); ++ } else if (strcmp(name, DSORGXATTR) == 0) { ++ lzds_dataset_get_is_PDS(ds, &ispds); ++ if (ispds) { ++ /* It is valid to ask for attributes of the directory */ ++ path_to_member_name(path, normds, sizeof(normds)); ++ if (strlen(normds)) ++ snprintf(buffer, sizeof(buffer), "PS"); ++ else ++ snprintf(buffer, sizeof(buffer), "PO"); ++ } else ++ snprintf(buffer, sizeof(buffer), "PS"); ++ } else ++ return -ENODATA; ++ ++ length = strlen(buffer); ++ if (size == 0) /* special case to query the necessary buffer size */ ++ return length; ++ if (size < length) ++ return -ERANGE; ++ strcpy(value, buffer); ++ return length; ++ ++} ++ ++#endif /* HAVE_SETXATTR */ ++ ++ ++static struct fuse_operations rdf_oper = { ++ .getattr = zdsfs_getattr, ++ .statfs = zdsfs_statfs, ++ .readdir = zdsfs_readdir, ++ .open = zdsfs_open, ++ .release = zdsfs_release, ++ .read = zdsfs_read, ++#ifdef HAVE_SETXATTR ++ .listxattr = zdsfs_listxattr, ++ .getxattr = zdsfs_getxattr, ++ /* no setxattr, removexattr since our xattrs are virtual */ ++#endif ++}; ++ ++ ++static int zdsfs_verify_datasets(void) ++{ ++ int allcomplete, rc; ++ struct dataset *ds; ++ char *dsname; ++ struct dsiterator *dsit; ++ int iscomplete, issupported; ++ ++ allcomplete = 1; ++ ++ rc = lzds_zdsroot_alloc_dsiterator(zdsfsinfo.zdsroot, &dsit); ++ if (rc) ++ return ENOMEM; ++ while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) { ++ lzds_dataset_get_is_complete(ds, &iscomplete); ++ if (!iscomplete) { ++ lzds_dataset_get_name(ds, &dsname); ++ fprintf(stderr, "Warning: Data set %s is not " ++ "complete\n", dsname); ++ allcomplete = 0; ++ continue; ++ } ++ lzds_dataset_get_is_supported(ds, &issupported); ++ if (!issupported) { ++ lzds_dataset_get_name(ds, &dsname); ++ fprintf(stderr, "Warning: Data set %s is not " ++ "supported\n", dsname); ++ } ++ } ++ lzds_dsiterator_free(dsit); ++ ++ if (!allcomplete && zdsfsinfo.allow_inclomplete_multi_volume) { ++ fprintf(stderr, "Continue operation with incomplete data" ++ " sets\n"); ++ return 0; ++ } ++ ++ if (allcomplete) { ++ return 0; ++ } else { ++ fprintf(stderr, ++ "Error: Some multi volume data sets are not complete.\n" ++ "Specify option 'ignore_incomplete' to allow operation " ++ "with incomplete data sets, or add missing volumes.\n"); ++ return EPROTO; ++ } ++}; ++ ++ ++static int zdsfs_create_meta_data_buffer(struct zdsfs_info *info) ++{ ++ char *mbrname; ++ char *dsname; ++ struct dataset *ds; ++ struct dsiterator *dsit; ++ struct memberiterator *it; ++ struct pdsmember *member; ++ int rc; ++ int ispds, issupported; ++ ++ char buffer[200]; /* large enough for one line of meta data */ ++ char recfm[20]; ++ char *temp; ++ char *metadata; ++ size_t metasize; /* total size of meta data buffer */ ++ size_t metaused; /* how many bytes of buffer are already filled */ ++ size_t count; ++ format1_label_t *f1; ++ ++ metadata = malloc(4096); ++ if (!metadata) ++ return -ENOMEM; ++ metasize = 4096; ++ metaused = 0; ++ metadata[metaused] = 0; ++ ++ rc = lzds_zdsroot_alloc_dsiterator(zdsfsinfo.zdsroot, &dsit); ++ if (rc) { ++ rc = -ENOMEM; ++ goto error; ++ } ++ while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) { ++ lzds_dataset_get_is_supported(ds, &issupported); ++ if (!issupported) ++ continue; ++ lzds_dataset_get_name(ds, &dsname); ++ lzds_dataset_get_format1_dscb(ds, &f1); ++ lzds_DS1RECFM_to_recfm(f1->DS1RECFM, recfm); ++ lzds_dataset_get_is_PDS(ds, &ispds); ++ count = snprintf(buffer, sizeof(buffer), ++ "dsn=%s,recfm=%s,lrecl=%u," ++ "dsorg=%s\n", ++ dsname, recfm, f1->DS1LRECL, ++ ispds ? "PO" : "PS"); ++ if (count >= sizeof(buffer)) { /* just a sanity check */ ++ count = sizeof(buffer) - 1; ++ buffer[count] = 0; ++ } ++ if (metaused + count + 1 > metasize) { ++ temp = realloc(metadata, metasize + 4096); ++ if (!temp) { ++ rc = -ENOMEM; ++ goto error; ++ } ++ metadata = temp; ++ metasize += 4096; ++ } ++ memcpy(&metadata[metaused], buffer, count + 1); ++ metaused += count; ++ ++ /* if the dataset is a PDS then we need to process all members ++ * of the PDS, otherwise continue with the next dataset */ ++ if (!ispds) ++ continue; ++ ++ rc = lzds_dataset_alloc_memberiterator(ds, &it); ++ if (rc) { ++ rc = -ENOMEM; ++ goto error; ++ } ++ while (!lzds_memberiterator_get_next_member(it, &member)) { ++ lzds_pdsmember_get_name(member, &mbrname); ++ count = snprintf(buffer, sizeof(buffer), ++ "dsn=%s(%s),recfm=%s,lrecl=%u," ++ "dsorg=PS\n", ++ dsname, mbrname, recfm, f1->DS1LRECL); ++ if (count >= sizeof(buffer)) { ++ count = sizeof(buffer) - 1; ++ buffer[count] = 0; ++ } ++ if (metaused + count + 1 > metasize) { ++ temp = realloc(metadata, metasize + 4096); ++ if (!temp) { ++ rc = -ENOMEM; ++ goto error; ++ } ++ metadata = temp; ++ metasize += 4096; ++ } ++ memcpy(&metadata[metaused], buffer, count + 1); ++ metaused += count; ++ } ++ lzds_memberiterator_free(it); ++ it = NULL; ++ } ++ lzds_dsiterator_free(dsit); ++ dsit = NULL; ++ ++ if (info->metadata) ++ free(info->metadata); ++ info->metadata = metadata; ++ info->metasize = metasize; ++ info->metaused = metaused; ++ info->metatime = time(NULL); ++ return 0; ++ ++error: ++ free(metadata); ++ lzds_dsiterator_free(dsit); ++ lzds_memberiterator_free(it); ++ return rc; ++}; ++ ++ ++ ++enum { ++ KEY_HELP, ++ KEY_VERSION, ++ KEY_DEVFILE, ++ KEY_TRACKS, ++ KEY_SEEKBUFFER, ++}; ++ ++#define ZDSFS_OPT(t, p, v) { t, offsetof(struct zdsfs_info, p), v } ++ ++static const struct fuse_opt zdsfs_opts[] = { ++ FUSE_OPT_KEY("-h", KEY_HELP), ++ FUSE_OPT_KEY("--help", KEY_HELP), ++ FUSE_OPT_KEY("-v", KEY_VERSION), ++ FUSE_OPT_KEY("--version", KEY_VERSION), ++ FUSE_OPT_KEY("-l %s", KEY_DEVFILE), ++ FUSE_OPT_KEY("tracks=", KEY_TRACKS), ++ FUSE_OPT_KEY("seekbuffer=", KEY_SEEKBUFFER), ++ ZDSFS_OPT("rdw", keepRDW, 1), ++ ZDSFS_OPT("ignore_incomplete", allow_inclomplete_multi_volume, 1), ++ FUSE_OPT_END ++}; ++ ++ ++static void usage(const char *progname) ++{ ++ fprintf(stderr, ++"Usage: %s []\n" ++"\n" ++"Use the zdsfs command to provide read access to data sets stored on one or\n" ++"more z/OS DASD devices.\n\n" ++"General options:\n" ++" -o opt,[opt...] Mount options\n" ++" -h --help Print help, then exit\n" ++" -v --version Print version, then exit\n" ++"\n" ++"ZDSFS options:\n" ++" -l list_file Text file that contains a list of DASD device" ++" nodes\n" ++" -o rdw Keep record descriptor words in byte stream\n" ++" -o ignore_incomplete Continue processing even if parts of a multi" ++" volume\n" ++" data set are missing\n" ++" -o tracks=N Size of the track buffer in tracks (default 128)\n" ++" -o seekbuffer=S Upper limit in bytes for the seek history buffer\n" ++" size (default 1048576)\n", progname); ++} ++ ++static void zdsfs_process_device(const char *device) ++{ ++ struct dasd *newdasd; ++ struct errorlog *log; ++ int rc; ++ ++ rc = lzds_zdsroot_add_device(zdsfsinfo.zdsroot, device, &newdasd); ++ if (rc) { ++ fprintf(stderr, "error when adding device %s: %s\n", device, ++ strerror(rc)); ++ lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log); ++ lzds_errorlog_fprint(log, stderr); ++ exit(1); ++ } ++ zdsfsinfo.devcount++; ++ rc = lzds_dasd_read_vlabel(newdasd); ++ if (rc) { ++ fprintf(stderr, "error when reading volume label from " ++ "device %s: %s\n", device, strerror(rc)); ++ lzds_dasd_get_errorlog(newdasd, &log); ++ 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); ++ } ++} ++ ++static void zdsfs_process_device_file(const char *devfile) ++{ ++ struct stat sb; ++ int fd; ++ ssize_t count; ++ size_t size, residual; ++ char *buffer; ++ char *runbuf; ++ char *token; ++ ++ fd = open(devfile, O_RDONLY); ++ if (fd < 0) { ++ fprintf(stderr, "could not open file %s: %s\n", ++ devfile, strerror(errno)); ++ exit(1); ++ } ++ if (fstat(fd, &sb) < 0) { ++ fprintf(stderr, "could not stat file %s: %s\n", ++ devfile, strerror(errno)); ++ exit(1); ++ } ++ if (!S_ISREG(sb.st_mode)) { ++ fprintf(stderr, "not a regular file %s\n", devfile); ++ exit(1); ++ } ++ if (sb.st_size) { ++ size = (size_t)sb.st_size + 1; ++ buffer = malloc(size); ++ bzero(buffer, size); ++ if (!buffer) { ++ fprintf(stderr, "could not allocate memory to buffer" ++ " file %s\n", devfile); ++ exit(1); ++ } ++ } else ++ return; ++ ++ count = 0; ++ residual = (size_t)sb.st_size; ++ while (residual) { ++ count = read(fd, buffer, residual); ++ if (count < 0) { ++ fprintf(stderr, "error when reading file %s: %s\n", ++ devfile, strerror(errno)); ++ exit(1); ++ } ++ if (!count) /* EOF */ ++ residual = 0; ++ residual -= count; ++ } ++ ++ runbuf = buffer; ++ while ((token = strsep(&runbuf, " \t\n"))) { ++ /* if several delimiters follow after another, token points ++ * to an empty string ++ */ ++ if (!strlen(token)) ++ continue; ++ ++ if (stat(token, &sb) < 0) { ++ fprintf(stderr, "could not stat device %s from" ++ " device list, %s\n", token, strerror(errno)); ++ exit(1); ++ } ++ if (S_ISBLK(sb.st_mode)) ++ zdsfs_process_device(token); ++ } ++ free(buffer); ++} ++ ++static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key, ++ struct fuse_args *outargs) ++{ ++ struct stat sb; ++ unsigned long tracks_per_frame; ++ unsigned long long seek_buffer_size; ++ const char *value; ++ char *endptr; ++ ++ switch (key) { ++ case FUSE_OPT_KEY_OPT: ++ return 1; ++ case FUSE_OPT_KEY_NONOPT: ++ if (stat(arg, &sb) < 0) { ++ fprintf(stderr, "could not stat device %s, %s\n", ++ arg, strerror(errno)); ++ return 1; ++ } ++ if (S_ISBLK(sb.st_mode)) { ++ zdsfs_process_device(arg); ++ return 0; ++ } ++ /* not a block device, so let fuse parse it */ ++ return 1; ++ case KEY_DEVFILE: ++ /* note that arg starts with "-l" */ ++ zdsfs_process_device_file(arg + 2); ++ return 0; ++ case KEY_TRACKS: ++ value = arg + strlen("tracks="); ++ /* strtoul does not complain about negative values */ ++ if (*value == '-') { ++ errno = EINVAL; ++ } else { ++ errno = 0; ++ tracks_per_frame = strtoul(value, &endptr, 10); ++ } ++ if (!errno && tracks_per_frame <= UINT_MAX) ++ zdsfsinfo.tracks_per_frame = tracks_per_frame; ++ else ++ errno = ERANGE; ++ if (errno || (endptr && (*endptr != '\0'))) { ++ fprintf(stderr, "Invalid value '%s' for option " ++ "'tracks'\n", value); ++ exit(1); ++ } ++ return 0; ++ case KEY_SEEKBUFFER: ++ value = arg + strlen("seekbuffer="); ++ /* strtoull does not complain about negative values */ ++ if (*value == '-') { ++ errno = EINVAL; ++ } else { ++ errno = 0; ++ seek_buffer_size = strtoull(value, &endptr, 10); ++ } ++ if (errno || (endptr && (*endptr != '\0'))) { ++ fprintf(stderr, "Invalid value '%s' for option " ++ "'seekbuffer'\n", value); ++ exit(1); ++ } ++ zdsfsinfo.seek_buffer_size = seek_buffer_size; ++ return 0; ++ case KEY_HELP: ++ usage(outargs->argv[0]); ++ fuse_opt_add_arg(outargs, "-ho"); ++ /* call fuse_main to let library print fuse options */ ++ fuse_main(outargs->argc, outargs->argv, &rdf_oper, NULL); ++ exit(0); ++ case KEY_VERSION: ++ fprintf(stderr, COMP "FUSE file system for z/OS data set access" ++ ", program version %s\n", RELEASE_STRING); ++ fprintf(stderr, "Copyright IBM Corp. 2013\n"); ++ exit(0); ++ default: ++ fprintf(stderr, "Unknown argument key %x\n", key); ++ exit(1); ++ } ++} ++ ++ ++int main(int argc, char *argv[]) ++{ ++ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); ++ int rc; ++ ++ bzero(&zdsfsinfo, sizeof(zdsfsinfo)); ++ zdsfsinfo.keepRDW = 0; ++ zdsfsinfo.allow_inclomplete_multi_volume = 0; ++ zdsfsinfo.tracks_per_frame = 128; ++ zdsfsinfo.seek_buffer_size = 1048576; ++ ++ rc = lzds_zdsroot_alloc(&zdsfsinfo.zdsroot); ++ if (rc) { ++ fprintf(stderr, "Could not allocate internal structures\n"); ++ exit(1); ++ } ++ if (fuse_opt_parse(&args, &zdsfsinfo, zdsfs_opts, ++ zdsfs_process_args) == -1) { ++ fprintf(stderr, "Failed to parse option\n"); ++ exit(1); ++ } ++ ++ if (!zdsfsinfo.devcount) { ++ fprintf(stderr, "Please specify a block device\n"); ++ fprintf(stderr, "Try '%s --help' for more information\n", ++ argv[0]); ++ exit(1); ++ } ++ rc = zdsfs_verify_datasets(); ++ if (rc) ++ goto cleanup; ++ ++ rc = zdsfs_create_meta_data_buffer(&zdsfsinfo); ++ if (rc) ++ goto cleanup; ++ ++ rc = fuse_main(args.argc, args.argv, &rdf_oper, NULL); ++ ++cleanup: ++ lzds_zdsroot_free(zdsfsinfo.zdsroot); ++ ++ fuse_opt_free_args(&args); ++ return rc; ++} +-- +1.9.3 + + +From 3695fd9337d142046c0dd03793ac25c84b6a5b15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 14:59:54 +0200 +Subject: [PATCH 17/27] qethqoat: OSA-Express5S Support + +Summary: qethqoat: OSA-Express5S Support +Description: Add support for OSA-Express5s cards. +--- + qethqoat/qethqoat.c | 3 +++ + qethqoat/qethqoat.h | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/qethqoat/qethqoat.c b/qethqoat/qethqoat.c +index 9b6f6e0..6185da9 100644 +--- a/qethqoat/qethqoat.c ++++ b/qethqoat/qethqoat.c +@@ -199,6 +199,9 @@ static void print_physical(struct qeth_qoat_physical *phdr) + case OAT_OSA_GEN_OSAE4S: + osagen = "OSA-Express4S"; + break; ++ case OAT_OSA_GEN_OSAE5S: ++ osagen = "OSA-Express5S"; ++ break; + default: + sprintf(tmp, "unknown (0x%x)", phdr->osa_gen); + osagen = tmp; +diff --git a/qethqoat/qethqoat.h b/qethqoat/qethqoat.h +index 221c474..8e9ee8b 100644 +--- a/qethqoat/qethqoat.h ++++ b/qethqoat/qethqoat.h +@@ -54,6 +54,7 @@ struct qeth_qoat_physical { + + #define OAT_OSA_GEN_OSAE3 0x01 + #define OAT_OSA_GEN_OSAE4S 0x02 ++#define OAT_OSA_GEN_OSAE5S 0x03 + __u8 osa_gen; + #define OAT_PORT_SPEED_UNKNOWN 0x00 + #define OAT_PORT_SPEED_10mbs_half 0x01 +-- +1.9.3 + + +From 483b24d1ee7e4231f0e92b4d8a5b276dc41ec3df Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 15:00:32 +0200 +Subject: [PATCH 18/27] lszcrypt: Support EP11 crypto adapters + +Summary: lszcrypt: Support EP11 crypto adapters +Description: This feature extends the lszcrypt command by a new capability + to support EP11 coprocessor cards. If a crypto adapter configured + in EP11 mode is available, lszcrypt will expose the following new + capbility: "EP11 Secure Key". +--- + zconf/lszcrypt | 4 ++++ + zconf/lszcrypt.8 | 7 ++----- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/zconf/lszcrypt b/zconf/lszcrypt +index 2a371d4..99664bb 100755 +--- a/zconf/lszcrypt ++++ b/zconf/lszcrypt +@@ -33,10 +33,12 @@ CAP_RSA2K="RSA 2K Clear Key" + CAP_RSA4K="RSA 4K Clear Key" + CAP_CCA="CCA Secure Key" + CAP_RNG="Long RNG" ++CAP_EP11="EP11 Secure Key" + + let MASK_RSA4K=0x60000000 + let MASK_COPRO=0x10000000 + let MASK_ACCEL=0x08000000 ++let MASK_EP11=0x04000000 + + function print_usage() { + cat <<-EOF +@@ -148,6 +150,8 @@ show_capability() { + CAPS+="$CAP_RSA4K\n" + CAPS+="$CAP_CCA\n" + CAPS+="$CAP_RNG" ++ elif (( FUNC_VAL&$MASK_EP11 )) ; then ++ CAPS+="$CAP_EP11" + else + CAPS="Detailed capability information for $CARD" + CAPS+=" (hardware type $HWTYPE) is not available." +diff --git a/zconf/lszcrypt.8 b/zconf/lszcrypt.8 +index e8ca6ce..3b9c122 100644 +--- a/zconf/lszcrypt.8 ++++ b/zconf/lszcrypt.8 +@@ -69,6 +69,8 @@ RSA 4K Clear Key + .IP "o" + CCA Secure Key + .IP "o" ++EP11 Secure Key ++.IP "o" + Long RNG + .RE + .TP 8 +@@ -111,8 +113,3 @@ Long RNG + .RE + .SH SEE ALSO + \fBchzcrypt\fR(8) +-.SH AUTHOR +-.nf +-This man-page was written by Ralph Wuerthner and +-Felix Beck . +-.fi +-- +1.9.3 + + +From 259ad6e825da6a18ef7c1a7ba1117c3a39d09736 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 15:01:04 +0200 +Subject: [PATCH 19/27] znetconf: Handle removal of offline'd ccwgroup devices + +Description: znetconf: Handle removal of offline'd ccwgroup devices +Symptom: "znetconf -r" does not remove ccwgroup device +Problem: Removing a device that is currently offline (e.g. that + was previously grouped only) results in an error. +Solution: If device is already offline, do not invoke the echo + command for offlining the device. +Reproduction: create a ccwgroup device manually and try to remove + this device with "znetconf -r" +--- + zconf/znetconf | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/zconf/znetconf b/zconf/znetconf +index 09f0904..383236d 100755 +--- a/zconf/znetconf ++++ b/zconf/znetconf +@@ -421,8 +421,14 @@ function ungroup_device() + echo "1" >> $DEVICE_UNGROUPFILE 2> /dev/null + case $? in + 0) +- echo "Successfully removed device" \ +- "$TARGET_DEVID ($DEVNAME)" ++ echo -n "Successfully removed device" \ ++ "$TARGET_DEVID" ++ if [ "$DEVNAME" == "" ] ++ then ++ echo ++ else ++ echo " ($DEVNAME)" ++ fi + ;; + *) + print_error "Failed to ungroup $TARGET_DEVID" +@@ -547,16 +553,19 @@ function switch_device() + + if [ -f $ONLINE_FILE ] + then +- echo "$SWITCHVALUE" >> $ONLINE_FILE +- case $? in +- 0) +- ;; +- *) +- print_error "Failed to make $CCWGROUPDEVNO" \ +- "$STATESTR" +- return 1 +- ;; +- esac ++ if [ "`cat $ONLINE_FILE`" != "$SWITCHVALUE" ] ++ then ++ echo "$SWITCHVALUE" >> $ONLINE_FILE ++ case $? in ++ 0) ++ ;; ++ *) ++ print_error "Failed to make $CCWGROUPDEVNO" \ ++ "$STATESTR" ++ return 1 ++ ;; ++ esac ++ fi + else + print_error "$ONLINE_FILE does not exist." + return 2 +-- +1.9.3 + + +From 8f8926ff0e84f024788eed10eca42391dc0f0936 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 15:01:43 +0200 +Subject: [PATCH 20/27] lsreipl: Show "fcp" instead of "fcp_dump" for fcp + re-IPL target + +Description: lsreipl: Show "fcp" instead of "fcp_dump" for fcp re-IPL target +Symptom: The lsreipl tools shows "fcp_dump" instead of "fcp" when + a FCP device is specified for re-IPL. +Problem: The lsreipl code hardcoded always sets "fcp_dump" instead + of "fcp" independent from the setting of + /sys/firmware/reipl/reipl_type. +Solution: Set "fcp" if /sys/firmware/reipl/reipl_type.contains "fcp". +Reproduction: 1) Change change re-IPL device to SCSI disk + # chreipl /dev/sda + 2) Call lsreipl + # lsreipl + Re-IPL type: fcp_dump + WWPN: 0x500507630508c1ae + LUN: 0x402040b400000000 + Device: 0.0.3d0b + .... +--- + ipl_tools/cmd_lsreipl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipl_tools/cmd_lsreipl.c b/ipl_tools/cmd_lsreipl.c +index 4daac82..39fc892 100644 +--- a/ipl_tools/cmd_lsreipl.c ++++ b/ipl_tools/cmd_lsreipl.c +@@ -135,7 +135,7 @@ void cmd_lsreipl(int argc, char *argv[]) + sizeof(reipl_type_str)); + + if (strcmp(reipl_type_str, "fcp") == 0) +- print_fcp(l.ipl_set, 1); ++ print_fcp(l.ipl_set, 0); + else if (strcmp(reipl_type_str, "fcp_dump") == 0) + print_fcp(l.ipl_set, 1); + else if (strcmp(reipl_type_str, "ccw") == 0) +-- +1.9.3 + + +From 401ff485af52aea17a41879573963d3bfcd7cd5a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 15:04:28 +0200 +Subject: [PATCH 21/27] iucvterm: provide ttyrun/iucvtty system instance units + +Description: iucvterm: provide ttyrun/iucvtty system instance units +Symptom: The /etc/inittab can no longer be used to configure ttyrun + or iucvtty instances. +Problem: The system daemon, systemd, replaces the traditional + /etc/inittab and SysV initialization approach. Also, systemd + uses a different configuration scheme to manage system services + and resources. +Solution: Provide systemd instance units to start getty programs on HVC + terminal devices with ttyrun. Also provide an instance unit to + configure and manage iucvtty instances. + + To install the systemd system units, specify the + SYSTEMDSYSTEMUNITDIR for the "make install" command. The value + of the SYSTEMDSYSTEMUNITDIR variable must point to the system + unit directory of your systemd installation (which is typically + "/usr/lib/systemd/system"). +Reproduction: None. +--- + Makefile | 2 +- + README | 4 ++++ + common.mak | 5 ++++- + systemd/Makefile | 31 +++++++++++++++++++++++++++++++ + systemd/iucvtty-login@.service.in | 26 ++++++++++++++++++++++++++ + systemd/ttyrun-getty@.service.in | 29 +++++++++++++++++++++++++++++ + 6 files changed, 95 insertions(+), 2 deletions(-) + create mode 100644 systemd/Makefile + create mode 100644 systemd/iucvtty-login@.service.in + create mode 100644 systemd/ttyrun-getty@.service.in + +diff --git a/Makefile b/Makefile +index 22728b9..d817f8a 100644 +--- a/Makefile ++++ b/Makefile +@@ -7,7 +7,7 @@ LIB_DIRS = libvtoc libu2s libutil libzds + SUB_DIRS = $(LIB_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 \ +- ziomon iucvterm hyptop cmsfs-fuse qethqoat zdsfs ++ ziomon iucvterm hyptop cmsfs-fuse qethqoat systemd zdsfs + + all: subdirs_make + +diff --git a/README b/README +index 5e05ec6..6250d46 100644 +--- a/README ++++ b/README +@@ -26,6 +26,10 @@ s390-tools (1.23.0) + - 60-readahead.rules: udev rules to set increased "default max readahead". + - 40-z90crypt.rules: udev rules for z90crypt driver + ++ * systemd units: ++ - iucvtty-login@.service: Instance unit to manage iucvtty instances ++ - ttyrun-getty@.service: Instance unit to manage ttyrun ++ + * zipl: + Make DASDs or tapes bootable for system IPL or system dump. + +diff --git a/common.mak b/common.mak +index 4373da5..16e2508 100644 +--- a/common.mak ++++ b/common.mak +@@ -55,8 +55,11 @@ LIBDIR = $(INSTROOT)/lib + SYSCONFDIR = $(INSTROOT)/etc + MANDIR = $(INSTROOT)/usr/share/man + TOOLS_LIBDIR = $(INSTROOT)/lib/s390-tools ++# Systemd support files are installed only if a directory is specified ++# for SYSTEMDSYSTEMUNITDIR ++SYSTEMDSYSTEMUNITDIR = + INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \ +- $(SYSCONFDIR) $(TOOLS_LIBDIR) ++ $(SYSCONFDIR) $(TOOLS_LIBDIR) $(SYSTEMDSYSTEMUNITDIR) + OWNER = $(shell id -un) + GROUP = $(shell id -gn) + export INSTROOT BINDIR LIBDIR MANDIR OWNER GROUP +diff --git a/systemd/Makefile b/systemd/Makefile +new file mode 100644 +index 0000000..da59f82 +--- /dev/null ++++ b/systemd/Makefile +@@ -0,0 +1,31 @@ ++include ../common.mak ++ ++SYSTEM_UNITS = ttyrun-getty@.service iucvtty-login@.service ++ ++all: ++ ++system_units: $(SYSTEM_UNITS) ++ ++check: ++ ++install: system_units ++ for unit in $(SYSTEM_UNITS); do \ ++ test -d "$(SYSTEMDSYSTEMUNITDIR)" || continue ; \ ++ $(INSTALL) -g $(GROUP) -o $(OWNER) \ ++ -m 644 $$unit $(SYSTEMDSYSTEMUNITDIR) ; \ ++ done ++ ++clean: ++ rm -f $(SYSTEM_UNITS) ++ ++%: %.in ++ real_bin_dir=`echo $(BINDIR) |sed 's#^$(INSTROOT)##'` ; \ ++ real_usrbin_dir=`echo $(USRBINDIR) |sed 's#^$(INSTROOT)##'` ; \ ++ real_usrsbin_dir=`echo $(USRSBINDIR) |sed 's#^$(INSTROOT)##'` ; \ ++ sed -e "s#@bin_path@#$$real_bin_dir#g" \ ++ -e "s#@usrbin_path@#$$real_usrbin_dir#g" \ ++ -e "s#@usrsbin_path@#$$real_usrsbin_dir#g" \ ++ -e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \ ++ < $< > $@ ++ ++.PHONY: all check install clean system_units +diff --git a/systemd/iucvtty-login@.service.in b/systemd/iucvtty-login@.service.in +new file mode 100644 +index 0000000..c721e47 +--- /dev/null ++++ b/systemd/iucvtty-login@.service.in +@@ -0,0 +1,26 @@ ++# Systemd unit for starting iucvtty instances. ++# ++# The instance ID corresponds to the terminal identifier for the iucvtty ++# instance. ++# ++ ++[Unit] ++Description=iucvtty login for terminal ID %I ++Documentation=man:iucvtty(1) man:iucvconn(1) man:login(1) ++After=systemd-user-sessions.service plymouth-quit-wait.service ++After=rc-local.service ++Before=getty.target ++Conflicts=rescue.service ++IgnoreOnIsolate=yes ++ ++ ++[Service] ++ExecStart=-@usrbin_path@/iucvtty %I ++KillMode=process ++Restart=always ++RestartSec=0 ++IgnoreSIGPIPE=no ++SendSIGHUP=yes ++ ++[Install] ++WantedBy=getty.target +diff --git a/systemd/ttyrun-getty@.service.in b/systemd/ttyrun-getty@.service.in +new file mode 100644 +index 0000000..8a979b0 +--- /dev/null ++++ b/systemd/ttyrun-getty@.service.in +@@ -0,0 +1,29 @@ ++# Systemd unit to start getty programs if a terminal is available using ++# the ttyrun program ++# ++ ++[Unit] ++Description=TTYRun on %I ++Documentation=man:ttyrun(8) man:agetty(8) ++BindTo=dev-%i.device ++After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service ++After=rc-local.service ++Before=getty.target ++IgnoreOnIsolate=yes ++ ++ ++[Service] ++Environment=TERM=linux ++ExecStart=-@bin_path@/ttyrun %I /sbin/agetty -L 115200,38400,9600 %I ++IgnoreSIGPIPE=no ++KillMode=process ++Restart=always ++RestartSec=0 ++SendSIGHUP=yes ++TTYPath=/dev/%I ++TTYReset=yes ++TTYVHangup=yes ++UtmpIdentifier=%I ++ ++[Install] ++WantedBy=getty.target +-- +1.9.3 + + +From c7c67510fff9a980131e038180075f74ceb6a42a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 15:05:07 +0200 +Subject: [PATCH 22/27] lsqeth: put grep regex in quotes + +Description: lsqeth: put grep regex in quotes +Symptom: `lsqeth` output depends on the presence of '?' file +Problem: Regex for `grep` command was not enclosed in qoutes, + and could be expanded as a wildcard. This happened when + a file named '?' was present in the current working + directory. This lead to failure to detect the system's + qeth devices. +Solution: Put the regex argument of `grep` in single quotes. +Reproduction: Create a file named '?' in a directory, and issue + `lsqeth` command. It will not show any qeth devices. +--- + zconf/lsqeth | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/zconf/lsqeth b/zconf/lsqeth +index bcd6277..7845c68 100755 +--- a/zconf/lsqeth ++++ b/zconf/lsqeth +@@ -390,7 +390,7 @@ if [ $device != 0 ]; then + device_list="`ls -ld $interface_dir$device/device/cdev0`" + device_list="${device_list##*/}" + else +- device_list="`ls $device_dir | grep [?\.?\.*]`" ++ device_list="`ls $device_dir | grep '[?\.?\.*]'`" + fi + + device_list_temp="`ls $interface_dir`" +-- +1.9.3 + + +From d49bebf2f51c4cf10330ff08ace1d08ea235ed3b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Wed, 23 Jul 2014 15:05:58 +0200 +Subject: [PATCH 23/27] zipl: Fix virtblk device detection + +Description: zipl: Fix virtblk device detection +Symptom: SCSI backed virtblk devices are recognized as FBA + devices. +Problem: The zipl virtblk heuristic analyses the device + geometry and supposes SCSI backed devices as FBA + devices. +Solution: Remove the FBA heuristic. +Reproduction: Use zipl with SCSI backed virtblk devices. +--- + zipl/src/disk.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/zipl/src/disk.c b/zipl/src/disk.c +index 804300f..a8db97f 100644 +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -169,12 +169,6 @@ determine_virtblk_type(struct disk_info *data, struct stat *stats) + } + close(fd); + misc_free_temp_dev(device); +- } else if (data->geo.heads == 16) { +- error_text("Assume disk_type FBA for virtblk device, " +- "but please specify type manually"); +- data->geo.start >>= shift; +- disk_print_geo(data); +- rc = -1; + } else { + data->type = disk_type_scsi; + data->partnum = stats->st_rdev & SCSI_PARTN_MASK; +-- +1.9.3 + + +From 4f1b5f1d87d36f4418629835f81d20b6fc85a315 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 24 Jul 2014 09:53:14 +0200 +Subject: [PATCH 24/27] qethqoat: buffer overflow + +Description: qethqoat: buffer overflow +Symptom: If too long string is passed as the interface name to the + quethqoat command, the program abortw with the diagnostic + "*** buffer overflow detected ***: qethqoat terminated". +Problem: qethqoat requires the interface name as input parameter. + The value is copied into a buffer of the size allowed as + the maximum interface name length in the kernel. Because + strcpy() is used, parameter longer than the buffer results + in the buffer overflow. +Solution: the length is now checked to be in the range of allowed + kernel interface name lengths. And the interface name strcpy() + operation is replaced by the corresponding strncpy() operation + to avoid buffer overflow in case of invalid and too long + interface parameters. +Reproduction: Execute `quethqoat` command with the parameter longer than + 16 characters. +--- + qethqoat/qethqoat.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/qethqoat/qethqoat.c b/qethqoat/qethqoat.c +index 6185da9..3029441 100644 +--- a/qethqoat/qethqoat.c ++++ b/qethqoat/qethqoat.c +@@ -559,6 +559,8 @@ int main(int argc, char **argv) + return 1; + } else { + opts.ifname = argv[optind]; ++ if (strlen(opts.ifname) >= IFNAMSIZ) ++ return -EINVAL; + } + + oat_data.command = 0; +@@ -590,7 +592,7 @@ int main(int argc, char **argv) + return errno; + } + +- strcpy(ifr.ifr_name, opts.ifname); ++ strncpy(ifr.ifr_name, opts.ifname, IFNAMSIZ); + oat_data.command = opts.scope; + ifr.ifr_ifru.ifru_data = (void *)&oat_data; + +-- +1.9.3 + + +From 2fe663dc725cc225638b165a6bbfcf94805f9d60 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 3 Oct 2014 10:39:00 +0200 +Subject: [PATCH 25/27] dbginfo.sh: Add option to specify directory for data + collection + +Description: dbginfo.sh: Add option to specify directory for data collection +Symptom: Data collection via dbginfo.sh is not possible. +Problem: In some environments, the directory /tmp may not provide enough + space to run the data collection via dbginfo.sh properly. +Solution: Introducing an option '-d' for the user that allows to specify + a directory, where the data collection takes place and where + the final TAR archive is stored. The specified directory must + already exist, it is not created during the script execution. + If the parameter is not specified, the default directory /tmp + is used. +Reproduction: Run dbginfo.sh on a machine where the space in /tmp is very + limited (e.g. small size or file system is filled up) +--- + scripts/dbginfo.sh | 212 ++++++++++++++++++++++++++++----------------------- + scripts/dbginfo.sh.1 | 12 ++- + 2 files changed, 124 insertions(+), 100 deletions(-) + +diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh +index b290202..64fa622 100755 +--- a/scripts/dbginfo.sh ++++ b/scripts/dbginfo.sh +@@ -26,15 +26,115 @@ + LC_ALL=C + export LC_ALL + +-# The kernel release version as delivered from uname -r +-readonly KERNEL_RELEASE_VERSION=$(uname -r 2>/dev/null) ++# The general name of this script ++readonly SCRIPTNAME="${0##*/}" ++ ++ ++######################################## ++# print version info ++print_version() { ++ cat </dev/null)" -ne 0; then ++ echo "${SCRIPTNAME}: Error: You must be user root to run \"${SCRIPTNAME}\"!" ++ exit 1 ++fi ++ ++####################################### ++# Parsing the command line ++# ++paramWORKDIR_BASE="/tmp/" ++ ++while [ ${#} -gt 0 ]; do ++ case ${1} in ++ --help|-h) ++ print_usage ++ exit 0 ++ ;; ++ --version|-v) ++ print_version ++ exit 0 ++ ;; ++ --directory|-d) ++ paramWORKDIR_BASE=${2} ++ shift ++ ;; ++ -*|--*|*) ++ echo ++ echo "${SCRIPTNAME}: invalid option \"${1}\"" ++ echo "Try '${SCRIPTNAME} --help' for more information" ++ echo ++ exit 1 ++ ;; ++ esac ++ shift ++done ++ ++if test -z "${paramWORKDIR_BASE}"; then ++ echo "${SCRIPTNAME}: Error: No directory specified for data collection!" ++ echo ++ exit 1 ++fi ++if test ! -d "${paramWORKDIR_BASE}"; then ++ echo "${SCRIPTNAME}: Error: The specified directory \"${paramWORKDIR_BASE}\" does ont exist!" ++ echo ++ exit 1 ++fi ++ + + ######################################## + # Global used variables + # + +-# The general name of this script +-readonly SCRIPTNAME="${0##*/}" ++# The base working directory ++readonly WORKDIR_BASE="$(echo "${paramWORKDIR_BASE}" | sed -e 's#/$##')/" + + # The terminal + readonly TERMINAL=$(tty 2>/dev/null) +@@ -42,6 +142,9 @@ readonly TERMINAL=$(tty 2>/dev/null) + # The hostname of the system + readonly SYSTEMHOSTNAME=$(hostname -s 2>/dev/null) + ++# The kernel release version as delivered from uname -r ++readonly KERNEL_RELEASE_VERSION=$(uname -r 2>/dev/null) ++ + # The processor ID for the first processor + readonly PROCESSORID=$(grep -E ".*processor 0:.*" /proc/cpuinfo | sed 's/.*identification[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g') + +@@ -51,9 +154,6 @@ readonly PROCESSORVERSION=$(grep -E ".*processor 0:.*" /proc/cpuinfo | sed 's/.* + # The current date + readonly DATETIME=$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null) + +-# The base working directory +-readonly WORKDIR_BASE="/tmp/" +- + # The current working directory for the actual script execution + if test -z "${PROCESSORID}"; then + readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}" +@@ -70,6 +170,9 @@ readonly WORKARCHIVE="${WORKDIR_BASE}${WORKDIR_CURRENT}.tgz" + # The log file of activities from this script execution + readonly LOGFILE="${WORKPATH}dbginfo.log" + ++# The file to indicate that another instance of the script is already running ++readonly LOCKFILE="/tmp/${SCRIPTNAME}.lock" ++ + # File that includes output of Linux commands + readonly OUTPUT_FILE_CMD="${WORKPATH}runtime.out" + +@@ -625,54 +728,6 @@ call_collect_file() { + + + ######################################## +-# print version info +-print_version() { +- cat < "${LOCKFILE}" + fi + + if ! mkdir "${WORKPATH}" 2>/dev/null; then +@@ -777,7 +803,7 @@ environment_cleanup() + pr_stdout " Please remove the directory manually" + pr_stdout " " + fi +- if ! rm -f "${WORKDIR_BASE}${SCRIPTNAME}".lock 2>/dev/null; then ++ if ! rm -f "${LOCKFILE}" 2>/dev/null; then + pr_stdout " " + pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKDIR_BASE}${SCRIPTNAME}\" failed" + pr_stdout " Please remove the file manually" +@@ -832,14 +858,6 @@ pr_syslog_stdout() + ############################################################################### + # Running the script + +-commandline_parse "${@}" +- +-# Verification to run as root +-if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then +- echo "${SCRIPTNAME}: Error: You must be user root to run \"${SCRIPTNAME}\"!" +- exit 1 +-fi +- + environment_setup + print_version + +diff --git a/scripts/dbginfo.sh.1 b/scripts/dbginfo.sh.1 +index c0975cc..bd2b09b 100644 +--- a/scripts/dbginfo.sh.1 ++++ b/scripts/dbginfo.sh.1 +@@ -6,6 +6,8 @@ for debugging Linux on System z + + .SH SYNOPSIS + .br ++\fBdbginfo.sh\fP [OPTIONS] ++.br + \fBdbginfo.sh\fP {\-h|\-v} + + .SH DESCRIPTION +@@ -28,6 +30,10 @@ Print usage information, then exit. + \fB\-v\fP, \fB\-\-version\fP + Print version information, then exit. + ++.TP ++\fB\-d \fP, \fB\-\-directory \fP ++Specify the DIRECTORY where the data collection stores the temporary data and the final archive. The specified directory must already exist. If this parameter is not specified, /tmp is used by default. ++ + .SH FILES + A .tgz file of the form + .PP +@@ -37,12 +43,12 @@ A .tgz file of the form + + .fam T + .fi +-is generated and placed in the /tmp directory. ++is generated and placed in the /tmp directory or in the directory specified by the -d option. + + .SH EXAMPLE + Sample invocation: + .P +-[root@host]# dbginfo.sh ++[root@host]# dbginfo.sh \-d /data\-collection + .br + dbginfo.sh: Debug information script version %S390_TOOLS_VERSION% + .br +@@ -72,7 +78,7 @@ Finalizing: Creating archive with collected data + .PP + Collected data was saved to: + .br +- >> /tmp/DBGINFO\-2013\-10\-08\-10\-43\-16\-host\-012345.tgz << ++ >> /data\-collection/DBGINFO\-2013\-10\-08\-10\-43\-16\-host\-012345.tgz << + .SH HINTS + Run the script with root authority. + .br +-- +1.9.3 + + +From 71073da18298010780834af7d964b507e42db87a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 3 Oct 2014 10:39:30 +0200 +Subject: [PATCH 26/27] dbginfo.sh: Improve data collection for systemd and + lsdasd + +Description: dbginfo.sh: Improve data collection for systemd and lsdasd +Symptom: Data collection via dbginfo.sh is incomplete. +Problem: The very important data from systemd is not collected when + running the dbginfo.sh script. + Also, the lsdasd command should be called with the option '-u' + when running the script. +Solution: Adding data collection for systemd and lsdasd. +Reproduction: Run dbginfo.sh and check for data from systemd and 'lsdasd -u' +--- + scripts/dbginfo.sh | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh +index 64fa622..4cb9f3f 100755 +--- a/scripts/dbginfo.sh ++++ b/scripts/dbginfo.sh +@@ -347,6 +347,7 @@ CONFIGFILES="\ + /etc/sysconfig\ + /etc/sysctl.d\ + /etc/syslog*\ ++ /etc/systemd\ + /etc/udev*\ + /etc/xinet.d\ + /etc/*release\ +@@ -391,6 +392,7 @@ CMDS="uname -a\ + :lscpu -ae\ + :lsmem\ + :lsdasd\ ++ :lsdasd -u\ + :ziorep_config -ADM\ + :lsmod\ + :lsdev\ +@@ -419,6 +421,10 @@ CMDS="uname -a\ + :cat /root/.bash_history\ + :env\ + :journalctl --all --no-pager --since=$(date -d '5 day ago' +%Y-%m-%d) --until=now --lines=50000 > ${OUTPUT_FILE_JOURNALCTL}\ ++ :systemd-delta\ ++ :systemctl --all --no-pager show\ ++ :systemctl --all --no-pager list-units\ ++ :systemctl --all --no-pager list-unit-files\ + " + + ######################################## +-- +1.9.3 + + +From f2544e24b74251617f0c3f4200af7e7303612adb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Thu, 4 Dec 2014 10:47:49 +0100 +Subject: [PATCH 27/27] cpuplugd: allow more than 64 CPUs + +Description: cpuplugd: allow more than 64 CPUs +Symptom: If more than 64 CPUs are present, they will be mostly ignored, + but the 65th CPU may be set online/offline and it will not be + restored to its previous state after cpuplugd shutdown. +Problem: There is currently a hardcoded limit of 64 CPUs for the cpuplugd, + and also an off-by-one error in get_numcpus() with more than 64 + CPUs. +Solution: Remove the artificial and unnecessary limit of 64 CPUs. +Reproduction: Use the cpuplugd on a system with more than 64 CPUs. +--- + cpuplugd/config.c | 29 ++++++----------------------- + cpuplugd/cpu.c | 6 ++++-- + cpuplugd/cpuplugd.h | 1 - + 3 files changed, 10 insertions(+), 26 deletions(-) + +diff --git a/cpuplugd/config.c b/cpuplugd/config.c +index bc9b448..b9f53c1 100644 +--- a/cpuplugd/config.c ++++ b/cpuplugd/config.c +@@ -210,7 +210,7 @@ void parse_configfile(char *file) + void check_config() + { + int cpuid; +- int lpar_status, error_counter; ++ int lpar_status; + + lpar_status = check_lpar(); + if (cfg.update < 0) +@@ -273,33 +273,16 @@ void check_config() + get_numcpus() >= cfg.cpu_min) { + cpuplugd_debug("The number of online cpus is below " + "the minimum and will be increased.\n"); +- error_counter = 0; + cpuid = 0; + while (get_num_online_cpus() < cfg.cpu_min && +- cpuid < get_numcpus()) { ++ cpuid < get_numcpus()) { + if (is_online(cpuid) == 1) { + cpuid++; + continue; + } +- if (cpuid < get_numcpus()) { +- cpuplugd_debug("cpu with id %d is " +- "currently offline and " +- "will be enabled\n", +- cpuid); +- if (hotplug(cpuid) == -1) +- error_counter++; +- } +- /* +- * Break because we tried at least to enable +- * every cpu once, but failed +- */ +- if (error_counter == MAX_CPU) +- cpuplugd_exit("Failed to initialize " +- "the minimum amount of cpus. " +- "This probably means that you " +- "specified more cpus in the " +- "cpu_min variable than " +- "currently exist.\n"); ++ cpuplugd_debug("cpu with id %d is currently offline " ++ "and will be enabled\n", cpuid); ++ hotplug(cpuid); + cpuid++; + } + } +@@ -308,7 +291,7 @@ void check_config() + " and will be decreased.\n"); + cpuid = 0; + while (get_num_online_cpus() > cfg.cpu_max && +- cpuid < MAX_CPU) { ++ cpuid < get_numcpus()) { + if (is_online(cpuid) != 1) { + cpuid++; + continue; +diff --git a/cpuplugd/cpu.c b/cpuplugd/cpu.c +index 2646844..a4456cf 100644 +--- a/cpuplugd/cpu.c ++++ b/cpuplugd/cpu.c +@@ -22,11 +22,13 @@ int get_numcpus() + char path[PATH_MAX]; + int number = 0; + +- for (i = 0; i <= MAX_CPU; i++) { ++ for (i = 0; ; i++) { + /* check whether file exists and is readable */ + sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i); + if (access(path, R_OK) == 0) + number++; ++ else ++ break; + } + return number; + } +@@ -185,7 +187,7 @@ void reactivate_cpus() + */ + if (num_cpu_start == 0) + return; +- while (get_num_online_cpus() != num_cpu_start && cpuid < MAX_CPU) { ++ while (get_num_online_cpus() != num_cpu_start && cpuid < get_numcpus()) { + nc = get_num_online_cpus(); + if (nc == num_cpu_start) + return; +diff --git a/cpuplugd/cpuplugd.h b/cpuplugd/cpuplugd.h +index a786607..243ca4d 100644 +--- a/cpuplugd/cpuplugd.h ++++ b/cpuplugd/cpuplugd.h +@@ -22,7 +22,6 @@ + #include + + #define NAME "cpuplugd" +-#define MAX_CPU 64 /* max amount of possible cpus */ + #define MAX_HISTORY 100 + #define PIDFILE "/var/run/cpuplugd.pid" + #define LOCKFILE "/var/lock/cpuplugd.lock" +-- +1.9.3 diff --git a/s390utils.spec b/s390utils.spec index fbfa7f2..0352c1a 100644 --- a/s390utils.spec +++ b/s390utils.spec @@ -1,11 +1,11 @@ %define cmsfsver 1.1.8c -%define vipaver 2.0.4 +%define vipaver 2.1.0 Name: s390utils Summary: Utilities and daemons for IBM System/z Group: System Environment/Base Version: 1.23.0 -Release: 13%{?dist} +Release: 14%{?dist} Epoch: 2 License: GPLv2 and GPLv2+ and CPL ExclusiveArch: s390 s390x @@ -41,8 +41,6 @@ Patch1000: cmsfs-1.1.8-warnings.patch Patch1001: cmsfs-1.1.8-kernel26.patch Patch1002: cmsfs-1.1.8-use-detected-filesystem-block-size-on-FBA-devices.patch -Patch2000: src_vipa-2.0.4-locations.patch - Requires: s390utils-base = %{epoch}:%{version}-%{release} Requires: s390utils-osasnmpd = %{epoch}:%{version}-%{release} Requires: s390utils-cpuplugd = %{epoch}:%{version}-%{release} @@ -82,14 +80,6 @@ pushd cmsfs-%{cmsfsver} %patch1002 -p1 -b .use-detected-block-size popd -# -# src_vipa -# -pushd src_vipa-%{vipaver} -# fix location of the library -%patch2000 -p1 -b .locations -popd - # remove --strip from install find . -name Makefile | xargs sed -i 's/$(INSTALL) -s/$(INSTALL)/g' @@ -167,7 +157,7 @@ install -p -m 644 cmsfs-%{cmsfsver}/cmsfsck.8 $RPM_BUILD_ROOT%{_mandir}/man8 # src_vipa pushd src_vipa-%{vipaver} -make install LIBDIR=%{_libdir} SBINDIR=%{_bindir} INSTROOT=$RPM_BUILD_ROOT +make install LIBDIR=%{_libdir} SBINDIR=%{_bindir} INSTROOT=$RPM_BUILD_ROOT LDCONFIG=/bin/true popd # install usefull headers for devel subpackage @@ -730,6 +720,23 @@ This package contains the CMS file system based on FUSE. %{_bindir}/cmsfs-fuse %{_mandir}/man1/cmsfs-fuse.1* +# +# *********************** zdsfs package *********************** +# +%package zdsfs +License: GPLv2 +Summary: z/OS data set access based on FUSE +Group: System Environment/Base +BuildRequires: fuse-devel +Requires: fuse + +%description zdsfs +This package contains the z/OS data set access based on FUSE. + +%files zdsfs +%{_bindir}/zdsfs +%{_mandir}/man1/zdsfs.1* + # # *********************** devel package *********************** # @@ -746,6 +753,12 @@ User-space development files for the s390/s390x architecture. %changelog +* Wed Jan 28 2015 Dan Horák - 2:1.23.0-14 +- refresh from RHEL-7 + - update patches + - add zdsfs subpackage + - rebase src_vipa to 2.1.0 + * Thu Oct 09 2014 Dan Horák - 2:1.23.0-13 - update device_cio_free script - udpate Requires for ziomon subpackage diff --git a/sources b/sources index 1333117..75ed1af 100644 --- a/sources +++ b/sources @@ -1,3 +1,3 @@ 71a8ee5918f2c44c385fcfe8350cdc98 cmsfs-1.1.8c.tar.gz -ba42772e5b305b5e147344442cd70826 src_vipa-2.0.4.tar.gz d7c8f8d6213f0e61e24c393347dcd8b7 s390-tools-1.23.0.tar.bz2 +6011b33227d843a6e2f8144331f4b3d4 src_vipa-2.1.0.tar.gz diff --git a/src_vipa-2.0.4-locations.patch b/src_vipa-2.0.4-locations.patch deleted file mode 100644 index 799b0f4..0000000 --- a/src_vipa-2.0.4-locations.patch +++ /dev/null @@ -1,74 +0,0 @@ -From b2f1bf78400c686bbdbcf4c29fbbb93367abe409 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 25 Mar 2009 09:36:08 +0100 -Subject: [PATCH] fix location of the library - ---- - Makefile | 7 +++---- - 1 files changed, 3 insertions(+), 4 deletions(-) - -diff --git a/Makefile b/Makefile -index 669b6c6..d395fa8 100644 ---- a/Makefile -+++ b/Makefile -@@ -20,7 +20,8 @@ INSTALL=install - VERSION=2.0.4 - - # the path to the .so --SRC_VIPA_PATH=$(INSTROOT)/usr/lib -+LIBDIR=/usr/lib -+SRC_VIPA_PATH=$(INSTROOT)$(LIBDIR) - # the path to the starter script - SRC_VIPA_STARTER_PATH=$(INSTROOT)/usr/sbin - # path to man page -@@ -34,8 +35,7 @@ src_vipa.so: src_vipa.c - - src_vipa.sh: - echo '#!/bin/bash' > src_vipa.sh -- echo 'export LD_LIBRARY_PATH=$(SRC_VIPA_PATH):$$LD_LIBRARY_PATH' >> src_vipa.sh -- echo 'export LD_PRELOAD=$(SRC_VIPA_PATH)/src_vipa.so' >> src_vipa.sh -+ echo 'export LD_PRELOAD=$(LIBDIR)/src_vipa.so' >> src_vipa.sh - echo 'exec $$@' >> src_vipa.sh - chmod 755 src_vipa.sh - -@@ -44,7 +44,6 @@ install: src_vipa.so src_vipa.sh - $(INSTALL) -m 755 src_vipa.so $(SRC_VIPA_PATH) - $(INSTALL) -m 755 src_vipa.sh $(SRC_VIPA_STARTER_PATH) - $(INSTALL) -m 644 src_vipa.8 $(SRC_VIPA_MANPAGE_PATH)/man8 -- ldconfig - - clean: - rm -f src_vipa.{i,s,o,sh,so} core src_vipa-$(VERSION).tar.gz --- -1.6.0.6 - -From 5c21f29f4d9e82942a997775c111280b85d01bb8 Mon Sep 17 00:00:00 2001 -From: =?utf-8?q?Dan=20Hor=C3=A1k?= -Date: Wed, 22 Apr 2009 12:53:55 +0200 -Subject: [PATCH] make the man page path and script path configurable - ---- - Makefile | 6 ++++-- - 1 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/Makefile b/Makefile -index d395fa8..365472b 100644 ---- a/Makefile -+++ b/Makefile -@@ -23,9 +23,11 @@ VERSION=2.0.4 - LIBDIR=/usr/lib - SRC_VIPA_PATH=$(INSTROOT)$(LIBDIR) - # the path to the starter script --SRC_VIPA_STARTER_PATH=$(INSTROOT)/usr/sbin -+SBINDIR=/usr/sbin -+SRC_VIPA_STARTER_PATH=$(INSTROOT)$(SBINDIR) - # path to man page --SRC_VIPA_MANPAGE_PATH=$(INSTROOT)/usr/share/man -+MANDIR=/usr/share/man -+SRC_VIPA_MANPAGE_PATH=$(INSTROOT)$(MANDIR) - - all: src_vipa.so src_vipa.sh - --- -1.6.0.6 -