52a75a97d6
- update patches - add zdsfs subpackage - rebase src_vipa to 2.1.0
11320 lines
352 KiB
Diff
11320 lines
352 KiB
Diff
From 9b225fac81186176075f673dfe5cf8e373b2068a Mon Sep 17 00:00:00 2001
|
|
From: Dan Horak <dan@danny.cz>
|
|
Date: Sun, 20 Jul 2008 09:24:05 +0200
|
|
Subject: [PATCH 01/27] s390-tools-1.5.3-zipl-zfcpdump-2
|
|
|
|
---
|
|
common.mak | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/common.mak b/common.mak
|
|
index 44adc6e..4373da5 100644
|
|
--- a/common.mak
|
|
+++ b/common.mak
|
|
@@ -62,8 +62,8 @@ GROUP = $(shell id -gn)
|
|
export INSTROOT BINDIR LIBDIR MANDIR OWNER GROUP
|
|
|
|
# Special defines for zfcpdump
|
|
-ZFCPDUMP_DIR = /usr/local/share/zfcpdump
|
|
-ZFCPDUMP_IMAGE = zfcpdump.image
|
|
+ZFCPDUMP_DIR = /boot
|
|
+ZFCPDUMP_IMAGE = zfcpdump
|
|
ZFCPDUMP_RD = zfcpdump.rd
|
|
export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_RD
|
|
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From a3d9221076f9eb7cc8434baac71327f786351c63 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Thu, 23 Apr 2009 11:46:01 +0200
|
|
Subject: [PATCH 02/27] s390-tools-1.8.1-fdasd-su
|
|
|
|
---
|
|
fdasd/fdasd.c | 10 ++++++----
|
|
1 file changed, 6 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c
|
|
index ba22475..f2ac417 100644
|
|
--- a/fdasd/fdasd.c
|
|
+++ b/fdasd/fdasd.c
|
|
@@ -2229,10 +2229,12 @@ static void fdasd_get_geometry (fdasd_anchor_t *anc)
|
|
if (anc->verbose) printf("disk type check : ok\n");
|
|
|
|
if (dasd_info.FBA_layout != 0) {
|
|
- snprintf(err_str, ERROR_STRING_SIZE,
|
|
- "%s is not formatted with z/OS compatible "
|
|
- "disk layout!", options.device);
|
|
- fdasd_error(anc, wrong_disk_format, err_str);
|
|
+ if (!anc->silent) {
|
|
+ snprintf(err_str, ERROR_STRING_SIZE,
|
|
+ "%s is not formatted with z/OS compatible "
|
|
+ "disk layout!", options.device);
|
|
+ fdasd_error(anc, wrong_disk_format, err_str);
|
|
+ }
|
|
}
|
|
|
|
if (anc->verbose) printf("disk layout check : ok\n");
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From d13c754f68ea838a47b8125006b9b493cfbbb7f4 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Wed, 21 Aug 2013 12:13:30 +0200
|
|
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
|
|
Problem: The trace pipes for CPU tracing in sysfs can potentially block
|
|
dbginfo.
|
|
Solution: Switch over to make use of dd with the nonblock option to savely
|
|
collect the whole sysfs without keeping an exclusion list up to
|
|
date
|
|
Reproduction: Run dbginfo on a kernel that provides trace_pipe and
|
|
trace_pipe_raw
|
|
---
|
|
scripts/dbginfo.sh | 15 ++++++---------
|
|
1 file changed, 6 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh
|
|
index 6d07132..0ada40b 100755
|
|
--- a/scripts/dbginfo.sh
|
|
+++ b/scripts/dbginfo.sh
|
|
@@ -181,8 +181,8 @@ fi
|
|
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
|
|
|
|
@@ -492,12 +492,9 @@ collect_sysfs() {
|
|
mkdir -p "${WORKPATH}${dir_name}"
|
|
done
|
|
|
|
- find /sys -noleaf -type f -perm /444 \
|
|
- -a -not -name "*trace_pipe" \
|
|
- -a -not -path "*debug/hid*/events" \
|
|
- -a -not -path "*debug/usb/usbmon/*" 2>/dev/null | while IFS= read -r file_name; do
|
|
+ find /sys -noleaf -type f -perm /444 2>/dev/null | while IFS= read -r file_name; do
|
|
echo " ${file_name}"
|
|
- cp -P --preserve=links -L --parents "${file_name}" "${WORKPATH}"
|
|
+ dd if="${file_name}" iflag=nonblock of="${WORKPATH}${file_name}"
|
|
done
|
|
|
|
if test ${debugfs_mounted} -eq 1; then
|
|
@@ -574,7 +571,7 @@ call_run_command() {
|
|
|
|
# check if command exists
|
|
if ! which "${raw_cmd}" >/dev/null 2>&1; then
|
|
- # check if command is a builtin
|
|
+ # 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 >> "${logfile}"
|
|
@@ -722,7 +719,7 @@ environment_setup()
|
|
|
|
if test -e "${WORKDIR_BASE}${SCRIPTNAME}".lock; then
|
|
print_alreadyrunning
|
|
- exit 1
|
|
+ exit 1
|
|
else
|
|
touch "${WORKDIR_BASE}${SCRIPTNAME}".lock
|
|
fi
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 7d540e7f40c731092ac655d1d38af7d69ceee706 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Wed, 21 Aug 2013 12:13:58 +0200
|
|
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
|
|
Symptom: When specifying "--force" and afterwards the dump partition is
|
|
modified, for example by creating a swap partition, the dump
|
|
still fails with the message "Wrong signature".
|
|
Problem: The "larl" instruction was used to load an odd address which
|
|
results in an incorrect even address.
|
|
Solution: Load the correct even address.
|
|
Reproduction: 1) Create ECKD MV dump DASDs:
|
|
$ zipl -M mvdump.list --force
|
|
2) Modify partitions:
|
|
$ dd if=/dev/zero of=/dev/dasdx1
|
|
3) Create dump
|
|
-> Should work with this fix
|
|
---
|
|
zipl/boot/eckd2mvdump.S | 5 +++--
|
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/zipl/boot/eckd2mvdump.S b/zipl/boot/eckd2mvdump.S
|
|
index f1cec78..529d6b3 100644
|
|
--- a/zipl/boot/eckd2mvdump.S
|
|
+++ b/zipl/boot/eckd2mvdump.S
|
|
@@ -153,7 +153,7 @@ _dump_mem_64:
|
|
|
|
.Lcheck_sign:
|
|
larl %r7,.Lforce
|
|
- tm 0(%r7),0x01 # was zipl --force specified?
|
|
+ tm 1(%r7),0x01 # was zipl --force specified?
|
|
bo .Lheaders-0b(%r13) # yes, skip signature check
|
|
llgf %r2,.Ldev_start_blk-0b(%r13) # start block of partition
|
|
lghi %r3,TMP_PAGE_START # destination of read operation
|
|
@@ -576,8 +576,9 @@ _ioblock_64:
|
|
.byte 0x85, 0x40, 0xa2, 0xa3, 0x81, 0x94, 0x97, 0xa2
|
|
.byte 0x00
|
|
|
|
-.org (MVDUMP_TOOL_SIZE - 9)
|
|
+.org (MVDUMP_TOOL_SIZE - 10)
|
|
.Lforce:
|
|
+ .byte 0x00
|
|
.byte 0x00 # is set to 0x01 by zipl -f
|
|
.Lmem_upper_limit:
|
|
.long 0xffffffff,0xffffffff # can be used for memsize=xxx
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 21caf0d0dc05c5e950f369f72027a203a7d3e772 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Tue, 5 Nov 2013 12:23:18 +0100
|
|
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.
|
|
Problem: For each possible CPU the zfcpdump kernel consumes memory for
|
|
the per-CPU data structures. Since it only runs with one CPU
|
|
this is not necessary. Because only 32 MiB are available for
|
|
zfcpdump the per-CPU data should not be allocated.
|
|
Solution: Use the kernel parameter "possible_cpus=1".
|
|
Reproduction: To verify that the fix is included check that the zipl -D output
|
|
line "kernel parmline" contains "possible_cpus=1".
|
|
---
|
|
zipl/src/bootmap.c | 5 +++--
|
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c
|
|
index cc2ed16..68dffe1 100644
|
|
--- a/zipl/src/bootmap.c
|
|
+++ b/zipl/src/bootmap.c
|
|
@@ -603,10 +603,11 @@ create_dump_fs_parmline(const char* parmline, const char* root_dev,
|
|
if (!result)
|
|
return NULL;
|
|
snprintf(result, DUMP_PARAM_MAX_LEN, "%s%sroot=%s dump_part=%d "
|
|
- "dump_mem=%lld maxcpus=%d cgroup_disable=memory",
|
|
+ "dump_mem=%lld maxcpus=%d possible_cpus=%d "
|
|
+ "cgroup_disable=memory",
|
|
parmline ? parmline : "",
|
|
parmline ? " " : "", root_dev, part_num,
|
|
- (unsigned long long) mem, max_cpus);
|
|
+ (unsigned long long) mem, max_cpus, max_cpus);
|
|
result[DUMP_PARAM_MAX_LEN - 1] = 0;
|
|
return result;
|
|
}
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From d3792e20601152ac2deea8d592b9fc176590ec5f Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Tue, 19 Nov 2013 18:02:03 +0100
|
|
Subject: [PATCH 06/27] dbginfo.sh: enhancements for script execution and man
|
|
page
|
|
|
|
Description: dbginfo.sh: enhancements for script execution and man page
|
|
Symptom: The result of the data collection does not provide all required
|
|
information which is required to finally analyze the situation.
|
|
Problem: The execution of the script has the following issues
|
|
* The script does not verify if it is running for user root
|
|
* The script does not post any messages into syslog during
|
|
runtime. This makes it quite difficulty to verify, when the
|
|
data collection started and ended.
|
|
* The script does not run in a dedicated locale. The output of
|
|
various tools can include messages in the language which has
|
|
been set by the administrator.
|
|
* Some Linux on System z specific tools are not yet processed
|
|
during data collection, such as lsmem and lschp.
|
|
* Some important configuration files are still not collected,
|
|
such as openssl.conf and openssl.cnf.
|
|
* Some parts in the man page might not be shown properly
|
|
Solution: The following changes are implemented:
|
|
* Added verification if the script is executed for user root
|
|
* Added logging mechanism to print messages into syslog.
|
|
* Added statement for LC_ALL to set the "C" 'standard' locale
|
|
* Added lsmem and lschp for commands being executed
|
|
* Added openssl.conf and openssl.cnf to be collected as
|
|
configuration files
|
|
* Corrected some parts in the man page
|
|
Reproduction: Some information how to reproduce the issues
|
|
* Run the script as 'non-root' user. The output will not contain
|
|
all the important information.
|
|
* Run the script and verify if something is stated in syslog,
|
|
when the script is executed.
|
|
* Run the script on a system, where the locale is set to
|
|
'non-en' locale. Some out put of important commands will be
|
|
printed in the non-en locale.
|
|
* Run the data collection and verify that the output of lsmem,
|
|
and lschp and that the config files openssl.conf, and
|
|
openssl.cnf are not collected.
|
|
---
|
|
scripts/dbginfo.sh | 167 ++++++++++++++++++++++++++++++---------------------
|
|
scripts/dbginfo.sh.1 | 34 +++++------
|
|
2 files changed, 116 insertions(+), 85 deletions(-)
|
|
|
|
diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh
|
|
index 0ada40b..9b64076 100755
|
|
--- a/scripts/dbginfo.sh
|
|
+++ b/scripts/dbginfo.sh
|
|
@@ -28,6 +28,9 @@
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
###############################################################################
|
|
|
|
+# Switching to neutral locale
|
|
+export LC_ALL=C
|
|
+
|
|
# The kernel release version as delivered from uname -r
|
|
readonly KERNEL_RELEASE_VERSION="`uname -r 2>/dev/null`"
|
|
|
|
@@ -44,6 +47,9 @@ readonly TERMINAL="`tty 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'`
|
|
|
|
+# The processor version for the first processor
|
|
+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`
|
|
|
|
@@ -114,13 +120,13 @@ else
|
|
readonly LINUX_SUPPORT_SYSFSDBF=0
|
|
fi
|
|
|
|
-# Is this Linux on System z under z/VM (0=yes, 1=no)
|
|
-if grep -q 'z/VM' /proc/sysinfo 2>/dev/null; then
|
|
- readonly LINUX_ON_ZVM=0
|
|
+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'`;
|
|
else
|
|
- readonly LINUX_ON_ZVM=1
|
|
+ readonly RUNTIME_ENVIRONMENT="LPAR"
|
|
fi
|
|
|
|
+
|
|
########################################
|
|
|
|
# Collection of proc fs entries
|
|
@@ -181,8 +187,8 @@ fi
|
|
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
|
|
|
|
@@ -277,7 +283,9 @@ CMDS="uname -a\
|
|
:multipath -d\
|
|
:multipath -t\
|
|
:lsqeth\
|
|
+ :lschp\
|
|
:lscss\
|
|
+ :lsmem\
|
|
:lsdasd\
|
|
:ziorep_config -ADM\
|
|
:lsmod\
|
|
@@ -376,7 +384,7 @@ collect_cmdsout() {
|
|
local cmd
|
|
local ifs_orig="${IFS}"
|
|
|
|
- pr_log_stdout " 1 of ${COLLECTION_COUNT}: Collecting command output"
|
|
+ pr_syslog_stdout "1 of ${COLLECTION_COUNT}: Collecting command output"
|
|
|
|
IFS=:
|
|
for cmd in ${CMDS}; do
|
|
@@ -397,8 +405,8 @@ collect_vmcmdsout() {
|
|
local module_loaded=1
|
|
local ifs_orig="${IFS}"
|
|
|
|
- if test ${LINUX_ON_ZVM} -eq 0; then
|
|
- pr_log_stdout " 2 of ${COLLECTION_COUNT}: Collecting z/VM command output"
|
|
+ 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
|
|
cp_command="vmcp"
|
|
@@ -412,8 +420,8 @@ collect_vmcmdsout() {
|
|
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 "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 " "
|
|
return 1
|
|
fi
|
|
@@ -443,7 +451,7 @@ collect_vmcmdsout() {
|
|
rmmod vmcp
|
|
fi
|
|
else
|
|
- pr_log_stdout " 2 of ${COLLECTION_COUNT}: Running in LPAR, no z/VM command output collected"
|
|
+ pr_syslog_stdout "2 of ${COLLECTION_COUNT}: Collecting z/VM command output skipped - no z/VM environment"
|
|
fi
|
|
|
|
pr_log_stdout " "
|
|
@@ -454,7 +462,7 @@ collect_vmcmdsout() {
|
|
collect_procfs() {
|
|
local file_name
|
|
|
|
- pr_log_stdout " 3 of ${COLLECTION_COUNT}: Collecting procfs"
|
|
+ pr_syslog_stdout "3 of ${COLLECTION_COUNT}: Collecting procfs"
|
|
|
|
for file_name in ${PROCFILES}; do
|
|
call_collect_file "${file_name}"
|
|
@@ -473,7 +481,7 @@ collect_sysfs() {
|
|
|
|
# Requires kernel version newer then 2.4
|
|
if test ${LINUX_SUPPORT_SYSFS} -eq 0; then
|
|
- pr_log_stdout " 4 of ${COLLECTION_COUNT}: Collecting sysfs"
|
|
+ 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 ! grep -qE "${MOUNT_POINT_DEBUGFS}.*debugfs" /proc/mounts 2>/dev/null; then
|
|
@@ -481,7 +489,7 @@ collect_sysfs() {
|
|
sleep 2
|
|
debugfs_mounted=1;
|
|
else
|
|
- pr_log_stdout " WARNING: \"Unable to mount debugfs ${MOUNT_POINT_DEBUGFS}\""
|
|
+ pr_log_stdout "WARNING: \"Unable to mount debugfs ${MOUNT_POINT_DEBUGFS}\""
|
|
fi
|
|
fi
|
|
fi
|
|
@@ -493,7 +501,7 @@ collect_sysfs() {
|
|
done
|
|
|
|
find /sys -noleaf -type f -perm /444 2>/dev/null | while IFS= read -r file_name; do
|
|
- echo " ${file_name}"
|
|
+ echo " ${file_name}"
|
|
dd if="${file_name}" iflag=nonblock of="${WORKPATH}${file_name}"
|
|
done
|
|
|
|
@@ -501,7 +509,7 @@ collect_sysfs() {
|
|
umount "${MOUNT_POINT_DEBUGFS}"
|
|
fi
|
|
else
|
|
- pr_log_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 " "
|
|
@@ -512,7 +520,7 @@ collect_sysfs() {
|
|
collect_logfiles() {
|
|
local file_name
|
|
|
|
- pr_log_stdout " 5 of ${COLLECTION_COUNT}: Collecting log files"
|
|
+ pr_syslog_stdout "5 of ${COLLECTION_COUNT}: Collecting log files"
|
|
|
|
for file_name in ${LOGFILES}; do
|
|
call_collect_file "${file_name}"
|
|
@@ -521,11 +529,12 @@ collect_logfiles() {
|
|
pr_log_stdout " "
|
|
}
|
|
|
|
+
|
|
########################################
|
|
collect_configfiles() {
|
|
local file_name
|
|
|
|
- pr_log_stdout " 6 of ${COLLECTION_COUNT}: Collecting config files"
|
|
+ pr_syslog_stdout "6 of ${COLLECTION_COUNT}: Collecting config files"
|
|
|
|
for file_name in ${CONFIGFILES}; do
|
|
call_collect_file "${file_name}"
|
|
@@ -542,16 +551,16 @@ collect_osaoat() {
|
|
|
|
if which qethqoat >/dev/null 2>&1; then
|
|
if test -n "${network_devices}"; then
|
|
- pr_log_stdout " 7 of ${COLLECTION_COUNT}: Collecting osa oat output"
|
|
+ pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output"
|
|
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
|
|
else
|
|
- pr_log_stdout " 7 of ${COLLECTION_COUNT}: Collecting osa oat output skipped - no devices"
|
|
+ pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output skipped - no devices"
|
|
fi
|
|
else
|
|
- pr_log_stdout " 7 of ${COLLECTION_COUNT}: Collecting osa oat output skipped - not available"
|
|
+ pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output skipped - not available"
|
|
fi
|
|
|
|
pr_log_stdout " "
|
|
@@ -571,16 +580,16 @@ call_run_command() {
|
|
|
|
# check if command exists
|
|
if ! which "${raw_cmd}" >/dev/null 2>&1; then
|
|
- # check if command is a builtin
|
|
+ # 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 "WARNING: Command \"${raw_cmd}\" not available" >> "${logfile}"
|
|
echo >> "${logfile}"
|
|
return 1;
|
|
fi
|
|
fi
|
|
|
|
if ! eval "${cmd}" >> "${logfile}" 2>&1; then
|
|
- echo " WARNING: Command \"${cmd}\" failed" >> "${logfile}"
|
|
+ echo "WARNING: Command \"${cmd}\" failed" >> "${logfile}"
|
|
echo >> "${logfile}"
|
|
return 1
|
|
else
|
|
@@ -595,7 +604,7 @@ call_collect_file() {
|
|
local directory_name
|
|
local file_name="${1}"
|
|
|
|
- echo " ${file_name}"
|
|
+ echo " ${file_name}"
|
|
|
|
directory_name="`dirname \"${file_name}\" 2>/dev/null`"
|
|
if test ! -e "${WORKPATH}${directory_name}"; then
|
|
@@ -616,8 +625,8 @@ call_collect_file() {
|
|
# print version info
|
|
print_version() {
|
|
cat <<EOF
|
|
- ${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
|
- Copyright IBM Corp. 2002, 2013
|
|
+${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
|
+Copyright IBM Corp. 2002, 2013
|
|
EOF
|
|
}
|
|
|
|
@@ -631,30 +640,30 @@ print_usage()
|
|
cat <<EOF
|
|
|
|
|
|
- Usage: ${SCRIPTNAME} [OPTIONS]
|
|
+Usage: ${SCRIPTNAME} [OPTIONS]
|
|
|
|
- This script collects runtime, configuration and trace information about
|
|
- your Linux on System z installation for debugging purposes.
|
|
+This script collects runtime, configuration and trace information about
|
|
+your Linux on System z installation for debugging purposes.
|
|
|
|
- It also traces information about z/VM if the Linux runs under z/VM.
|
|
+It also traces information about z/VM if the Linux runs under z/VM.
|
|
|
|
|
|
- The collected information is written to a TAR archive named
|
|
+The collected information is written to a TAR archive named
|
|
|
|
- /tmp/DBGINFO-[date]-[time]-[hostname]-[processorid].tgz
|
|
+ /tmp/DBGINFO-[date]-[time]-[hostname]-[processorid].tgz
|
|
|
|
- where [date] and [time] are the date and time when debug data is collected.
|
|
- [hostname] indicates the hostname of the system the data was collected from.
|
|
- The [processorid] is taken from the processor 0 and indicates the processor
|
|
- identification.
|
|
+where [date] and [time] are the date and time when debug data is collected.
|
|
+[hostname] indicates the hostname of the system the data was collected from.
|
|
+The [processorid] is taken from the processor 0 and indicates the processor
|
|
+identification.
|
|
|
|
- Options:
|
|
+Options:
|
|
|
|
-h|--help print this help
|
|
-v|--version print version information
|
|
|
|
|
|
- Please report bugs to: linux390@de.ibm.com
|
|
+Please report bugs to: linux390@de.ibm.com
|
|
|
|
EOF
|
|
}
|
|
@@ -668,9 +677,9 @@ print_alreadyrunning() {
|
|
cat <<EOF
|
|
|
|
|
|
- Please check the system if another instance of ${SCRIPTNAME} is already
|
|
- running. If this is not the case, please remove the lock file
|
|
- '${WORKDIR_BASE}${SCRIPTNAME}.lock'.
|
|
+Please check the system if another instance of ${SCRIPTNAME} is already
|
|
+running. If this is not the case, please remove the lock file
|
|
+'${WORKDIR_BASE}${SCRIPTNAME}.lock'.
|
|
EOF
|
|
}
|
|
|
|
@@ -689,15 +698,15 @@ commandline_parse()
|
|
print_version
|
|
else
|
|
echo
|
|
- echo " ${SCRIPTNAME}: invalid option ${cmdline_arg1}"
|
|
- echo " Try '${SCRIPTNAME} --help' for more information"
|
|
+ echo "${SCRIPTNAME}: invalid option ${cmdline_arg1}"
|
|
+ echo "Try '${SCRIPTNAME} --help' for more information"
|
|
echo
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
elif test ${cmdline_count} -ge 1; then
|
|
echo
|
|
- echo " ERROR: Invalid number of arguments!"
|
|
+ echo "ERROR: Invalid number of arguments!"
|
|
echo
|
|
print_usage
|
|
exit 1
|
|
@@ -712,21 +721,21 @@ environment_setup()
|
|
if test ! -e "${WORKDIR_BASE}"; then
|
|
mkdir -p "${WORKDIR_BASE}"
|
|
elif test ! -d "${WORKDIR_BASE}"; then
|
|
- echo " ERROR: ${WORKDIR_BASE} exists but this is a file!"
|
|
- echo " Please make sure ${WORKDIR_BASE} is a directory."
|
|
+ echo "ERROR: ${WORKDIR_BASE} exists but this is a file!"
|
|
+ echo " Please make sure ${WORKDIR_BASE} is a directory."
|
|
exit 1
|
|
fi
|
|
|
|
if test -e "${WORKDIR_BASE}${SCRIPTNAME}".lock; then
|
|
print_alreadyrunning
|
|
- exit 1
|
|
+ exit 1
|
|
else
|
|
touch "${WORKDIR_BASE}${SCRIPTNAME}".lock
|
|
fi
|
|
|
|
if ! mkdir "${WORKPATH}" 2>/dev/null; then
|
|
- echo " ERROR: Target directory ${WORKPATH} already exists or"
|
|
- echo " ${WORKDIR_BASE} does not exist!"
|
|
+ echo "ERROR: Target directory ${WORKPATH} already exists or"
|
|
+ echo " ${WORKDIR_BASE} does not exist!"
|
|
exit 1
|
|
fi
|
|
}
|
|
@@ -736,18 +745,18 @@ environment_setup()
|
|
# create gzip-ped tar file
|
|
create_package()
|
|
{
|
|
- pr_stdout " Finalizing: Creating archive with collected data"
|
|
+ pr_stdout "Finalizing: Creating archive with collected data"
|
|
cd "${WORKDIR_BASE}"
|
|
|
|
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 " to provide enough free available space."
|
|
+ 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 " to provide enough free available space."
|
|
else
|
|
pr_stdout " "
|
|
- pr_stdout " Collected data was saved to:"
|
|
+ pr_stdout "Collected data was saved to:"
|
|
pr_stdout " >> ${WORKARCHIVE} <<"
|
|
fi
|
|
|
|
@@ -761,14 +770,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 "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 "WARNING: Deletion of ${WORKDIR_BASE}${SCRIPTNAME} failed"
|
|
+ pr_stdout "Please remove the file manually"
|
|
pr_stdout " "
|
|
fi
|
|
}
|
|
@@ -779,12 +788,13 @@ environment_cleanup()
|
|
emergency_exit()
|
|
{
|
|
pr_stdout " "
|
|
- pr_stdout " INFO: Data collection has been interrupted"
|
|
- pr_stdout " INFO: Cleanup of temporary collected data"
|
|
+ pr_stdout "INFO: Data collection has been interrupted"
|
|
+ pr_stdout "INFO: Cleanup of temporary collected data"
|
|
environment_cleanup
|
|
- pr_stdout " INFO: Emergency exit processed"
|
|
-
|
|
+ pr_stdout "INFO: Emergency exit processed"
|
|
+
|
|
pr_stdout " "
|
|
+ logger -t "${SCRIPTNAME}" "Data collection interrupted"
|
|
exit;
|
|
}
|
|
|
|
@@ -806,10 +816,27 @@ pr_log_stdout()
|
|
}
|
|
|
|
|
|
+########################################
|
|
+# Function to print to stdout and into log file when rediretion is active
|
|
+pr_syslog_stdout()
|
|
+{
|
|
+ echo "$@"
|
|
+ echo "$@" >&8
|
|
+ logger -t ${SCRIPTNAME} "$@"
|
|
+}
|
|
+
|
|
+
|
|
###############################################################################
|
|
# Running the script
|
|
|
|
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}!"
|
|
+ exit 1
|
|
+fi
|
|
+
|
|
environment_setup
|
|
print_version
|
|
|
|
@@ -820,11 +847,13 @@ exec 8>&1 9>&2 >${LOGFILE} 2>&1
|
|
trap emergency_exit 1 2 15
|
|
|
|
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 " Runtime environment = `test ${LINUX_ON_ZVM} -eq 0 && echo 'z/VM' || echo 'LPAR'`"
|
|
+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 ""
|
|
|
|
+logger -t "${SCRIPTNAME}" "Starting data collection"
|
|
+
|
|
collect_cmdsout
|
|
|
|
collect_vmcmdsout
|
|
@@ -845,6 +874,8 @@ create_package
|
|
|
|
environment_cleanup
|
|
|
|
+logger -t "${SCRIPTNAME}" "Data collection completed"
|
|
+
|
|
exec 1>&8 2>&9 8>&- 9>&-
|
|
|
|
#EOF
|
|
diff --git a/scripts/dbginfo.sh.1 b/scripts/dbginfo.sh.1
|
|
index cdef849..c0975cc 100644
|
|
--- a/scripts/dbginfo.sh.1
|
|
+++ b/scripts/dbginfo.sh.1
|
|
@@ -1,5 +1,5 @@
|
|
-.TH DBGINFO.SH 1 "November 2012" "s390-tools"
|
|
-
|
|
+.TH DBGINFO.SH 1 "October 2013" "s390-tools"
|
|
+
|
|
.SH NAME
|
|
dbginfo.sh \- collect runtime, configuration and trace information
|
|
for debugging Linux on System z
|
|
@@ -44,35 +44,35 @@ Sample invocation:
|
|
.P
|
|
[root@host]# dbginfo.sh
|
|
.br
|
|
- dbginfo.sh: Debug information script version %S390_TOOLS_VERSION%
|
|
+dbginfo.sh: Debug information script version %S390_TOOLS_VERSION%
|
|
.br
|
|
- Copyright IBM Corp. 2002, 2012
|
|
+Copyright IBM Corp. 2002, 2013
|
|
.PP
|
|
- Hardware platform = s390x
|
|
+Hardware platform = s390x
|
|
.br
|
|
- Kernel version = 3.0.13 (3.0.13\-0.27\-default)
|
|
+Kernel version = <kernel\-version>
|
|
.br
|
|
- Runtime environment = z/VM
|
|
+Runtime environment = z/VM
|
|
.PP
|
|
- 1 of 7: Collecting command output
|
|
+1 of 7: Collecting command output
|
|
.PP
|
|
- 2 of 7: Collecting z/VM command output
|
|
+2 of 7: Collecting z/VM command output
|
|
.PP
|
|
- 3 of 7: Collecting procfs
|
|
+3 of 7: Collecting procfs
|
|
.PP
|
|
- 4 of 7: Collecting sysfs
|
|
+4 of 7: Collecting sysfs
|
|
.PP
|
|
- 5 of 7: Collecting log files
|
|
+5 of 7: Collecting log files
|
|
.PP
|
|
- 6 of 7: Collecting config files
|
|
+6 of 7: Collecting config files
|
|
.PP
|
|
- 7 of 7: Collecting osa oat output skipped
|
|
+7 of 7: Collecting osa oat output skipped \- not available
|
|
.PP
|
|
- Finalizing: Creating archive with collected data
|
|
+Finalizing: Creating archive with collected data
|
|
.PP
|
|
- Collected data was saved to:
|
|
+Collected data was saved to:
|
|
.br
|
|
- >> /tmp/DBGINFO\-2012\-10\-14\-13\-10\-42-host-123456.tgz <<
|
|
+ >> /tmp/DBGINFO\-2013\-10\-08\-10\-43\-16\-host\-012345.tgz <<
|
|
.SH HINTS
|
|
Run the script with root authority.
|
|
.br
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 31cd858e82efd289c4ea8ea4801346746aefcd2c Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Tue, 19 Nov 2013 18:02:35 +0100
|
|
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'
|
|
Problem: The data collection of the dbginfo.sh script collects two times
|
|
entries from the sysfs. First, the script itself collects
|
|
all 'files' and later on, the script ziomon_fcpconf is
|
|
collecting a subset of the sysfs along with some additional
|
|
data. The additional data (information about the /dev entries)
|
|
is collected by the dbginfo.sh script also. Therefore, the
|
|
execution of ziomon_fcpconf is obsolete.
|
|
Solution: Avoid the execution of ziomon_fcpconf in the dbginfo.sh script
|
|
Reproduction: Run the dbginfo.sh script on a machine, where /tmp should be
|
|
able to cover the amount of data being collected. The machine
|
|
should have quite a lot of devices being attached.
|
|
---
|
|
scripts/dbginfo.sh | 4 ----
|
|
1 file changed, 4 deletions(-)
|
|
|
|
diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh
|
|
index 9b64076..e83774b 100755
|
|
--- a/scripts/dbginfo.sh
|
|
+++ b/scripts/dbginfo.sh
|
|
@@ -81,9 +81,6 @@ readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out"
|
|
# File that includes content of files from sysfs
|
|
readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfsfiles.out"
|
|
|
|
-# File that includes content of zFCP settings
|
|
-readonly OUTPUT_FILE_FCPCONF="${WORKPATH}scsi"
|
|
-
|
|
# File that includes content of OSA OAT
|
|
readonly OUTPUT_FILE_OSAOAT="${WORKPATH}osa_oat"
|
|
|
|
@@ -314,7 +311,6 @@ CMDS="uname -a\
|
|
:java -version\
|
|
:cat /root/.bash_history\
|
|
:env\
|
|
- :ziomon_fcpconf -o ${OUTPUT_FILE_FCPCONF}\
|
|
"
|
|
|
|
########################################
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 4009f4a16c96f7fee65d77de112ef61109fdc0bb Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Wed, 15 Jan 2014 15:08:29 +0100
|
|
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
|
|
lead to a 'double free or corruption' error in zipl.
|
|
Problem: The array to store automenu items is to small.
|
|
Solution: Correct the calculation for automenu array size.
|
|
Reproduction: Use zipl and build an automenu with a large number of
|
|
entries in the zipl configuration file.
|
|
---
|
|
zipl/src/scan.c | 12 ++++++++----
|
|
1 file changed, 8 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
|
|
index 597b01c..c357418 100644
|
|
--- a/zipl/src/scan.c
|
|
+++ b/zipl/src/scan.c
|
|
@@ -1672,10 +1672,14 @@ scan_build_automenu(struct scan_token* scan)
|
|
if (scan[i].id == scan_id_section_heading)
|
|
num_sections++;
|
|
}
|
|
- size = /* old scan array + delimiter */ i + 1 +
|
|
- /* defaultboot heading + keyword */ 2 +
|
|
- /* automenu heading + keywords */ 10 +
|
|
- /* missing target definitions */ num_sections * num_targets;
|
|
+ size = /* old scan array + delimiter */ i + 1 +
|
|
+ /* defaultboot heading */ 1 +
|
|
+ /* defaultmenu */ 1 +
|
|
+ /* menu heading */ 1 +
|
|
+ /* keyword default,prompt,timeout */ 3 +
|
|
+ /* target keywords*/ num_targets +
|
|
+ /* missing target definitions */ num_sections * num_targets +
|
|
+ /* number assigment */ num_sections;
|
|
size *= sizeof(struct scan_token);
|
|
new_scan = misc_malloc(size);
|
|
if (!new_scan)
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 5eca8bced9faf6a15bdb7a0c43b53b6817a53473 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Wed, 29 Jan 2014 10:37:03 +0100
|
|
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
|
|
message is written:
|
|
ERROR: open() source device '/sys/kernel/debug/zcore/mem' failed!
|
|
Problem: Because the "struct job_ipl_data" stack variable is not
|
|
initialized in add_dump_program() the "is_kdump" member can
|
|
be non-zero in case of zfcpdump. This makes the zfcpdump
|
|
code think that stand-alone kdump is triggered instead of
|
|
zfcpdump and then "zcore/mem" is not created by the kernel.
|
|
Solution: Initialize "struct job_ipl_data" with zeroes.
|
|
Reproduction: 1) Prepare SCSI disk with zipl:
|
|
# mount /dev/sda1 /mnt
|
|
# zipl -D /dev/sda1 -t /mnt
|
|
2) Trigger SCSI dump
|
|
|
|
Note: Because the problem occurs only if the stack variable is
|
|
not zero the problem will not occur every time.
|
|
---
|
|
zipl/src/bootmap.c | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c
|
|
index 68dffe1..d573eda 100644
|
|
--- a/zipl/src/bootmap.c
|
|
+++ b/zipl/src/bootmap.c
|
|
@@ -661,6 +661,7 @@ add_dump_fs_program(int fd, struct job_dump_fs_data* dump_fs,
|
|
int rc;
|
|
|
|
/* Convert fs dump job to IPL job */
|
|
+ memset(&ipl, 0, sizeof(ipl));
|
|
ipl.image = dump_fs->image;
|
|
ipl.image_addr = dump_fs->image_addr;
|
|
ipl.ramdisk = dump_fs->ramdisk;
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 89e147e16348335cdfe6438e43171e7848e94dce Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Mon, 3 Feb 2014 09:55:38 +0100
|
|
Subject: [PATCH 10/27] znetconf,lsqeth: Allow for 16-char network interface
|
|
name
|
|
|
|
Description: znetconf,lsqeth: Allow for 16-char network interface names
|
|
Symptom: In the output of `znetconf -c` interface name is truncated.
|
|
In the output of `lsqeth -p` interface name is jammed with the
|
|
following field (card type). This happens when the "new"
|
|
interface names are in play, i.e. "enccw0.0.e000" rather than
|
|
traditional "eth1".
|
|
Problem: In the functions that are doing formatted printout, the width
|
|
of the field reserved for the interface name is insufficinet.
|
|
Solution: Printing procedures in the `zneconf` and `lsqeth` are modified
|
|
to allow the interface names of up to 16 characters (IFNAMSIZ).
|
|
Reproduction: Rename some network interface to use more characters than
|
|
the usual 4. E.g. if the original interace name is "eth1",
|
|
use this command:
|
|
ip link set dev eth1 name 123456789ABCDEF
|
|
(Note: while the maximum length of the interface name is 16
|
|
chars, the `ip` command limits it to 15 chars.) Issue the
|
|
commands `znetconf -c` and `lsqeth -p`. Observe that the
|
|
interface name is truncated in the `znetconf` output, and
|
|
jammed together with the "card type" field in the `lsqeth`
|
|
output.
|
|
---
|
|
zconf/lsqeth | 6 +++---
|
|
zconf/znetconf | 2 +-
|
|
2 files changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/zconf/lsqeth b/zconf/lsqeth
|
|
index c3a0c64..bcd6277 100755
|
|
--- a/zconf/lsqeth
|
|
+++ b/zconf/lsqeth
|
|
@@ -238,7 +238,7 @@ function __print_proc_format
|
|
|
|
# print device data
|
|
printf '%-27s' "${format_array_print[0]}/${format_array_print[1]}/${format_array_print[2]}"
|
|
- for j in 6 11 15 5 7 11 5 5 6 5
|
|
+ for j in 6 17 15 5 7 11 5 5 6 5
|
|
do
|
|
if [ "$countc" -eq 3 ]; then
|
|
printf "%-${j}s" "x${format_array_print[$countc]}"
|
|
@@ -396,8 +396,8 @@ fi
|
|
device_list_temp="`ls $interface_dir`"
|
|
|
|
if [ $format = 1 ]; then
|
|
- echo "devices CHPID interface cardtype port chksum prio-q'ing rtr4 rtr6 lay'2 cnt"
|
|
- echo "-------------------------- ----- ---------- -------------- ---- ------ ---------- ---- ---- ----- -----"
|
|
+ echo "devices CHPID interface cardtype port chksum prio-q'ing rtr4 rtr6 lay'2 cnt"
|
|
+ echo "-------------------------- ----- ---------------- -------------- ---- ------ ---------- ---- ---- ----- -----"
|
|
fi
|
|
#
|
|
# list entries for device
|
|
diff --git a/zconf/znetconf b/zconf/znetconf
|
|
index 73bbe32..87c881b 100755
|
|
--- a/zconf/znetconf
|
|
+++ b/zconf/znetconf
|
|
@@ -737,7 +737,7 @@ function list_configured()
|
|
supress_header=$1
|
|
fi
|
|
|
|
- local LIST_FORMAT_STRING="%-26.26s %-7.7s %-14.14s %5.5s %-4.4s %-11.11s %-7.7s\n"
|
|
+ local LIST_FORMAT_STRING="%-26.26s %-7.7s %-14.14s %5.5s %-4.4s %-16.16s %-7.7s\n"
|
|
if [ $supress_header -eq 0 ]
|
|
then
|
|
printf "$LIST_FORMAT_STRING" "Device IDs" "Type" \
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 78560f75fa2ce043ff63647cc1618f69251dbbf7 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Mon, 10 Feb 2014 10:20:51 +0100
|
|
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.
|
|
This happens when the "new" interface names are in play,
|
|
i.e. "enccw0.0.e000" rather than traditional "eth1".
|
|
Problem: In the function that extracts the interface name from the
|
|
"$CFGLINE" string, only 11 characters of the interface name
|
|
are extracted.
|
|
Solution: Modify the function to extract 16 chacaters of the interface
|
|
name.
|
|
Reproduction: Rename some network interface to use more characters than
|
|
the usual 4. E.g. if the original interace name is "eth1",
|
|
use this command:
|
|
ip link set dev eth1 name 123456789ABCDEF
|
|
(Note: while the maximum length of the intrface name is 16
|
|
chars, the `ip` command limits it to 15 chars.)
|
|
Issue command `znetconf -r <interface-name>`. Observe that
|
|
interface name is truncated in the output.
|
|
---
|
|
zconf/znetconf | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/zconf/znetconf b/zconf/znetconf
|
|
index 87c881b..09f0904 100755
|
|
--- a/zconf/znetconf
|
|
+++ b/zconf/znetconf
|
|
@@ -1092,7 +1092,7 @@ function ask_for_remove()
|
|
function extract_interface_name()
|
|
{
|
|
local CFGLINE="$1"
|
|
- local IF_NAME=$(expr substr "$CFGLINE" 62 11)
|
|
+ local IF_NAME=$(expr substr "$CFGLINE" 62 16)
|
|
REPLY=${IF_NAME%% *}
|
|
return 0
|
|
}
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 883724cff09a02a19268a47102816e161a4b01af Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Mon, 10 Feb 2014 10:21:23 +0100
|
|
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.
|
|
This happens when the "new" interface names are in play,
|
|
i.e. "enccw0.0.e000" rather than traditional "eth1".
|
|
Problem: In the functions that are doing formatted printout, the width
|
|
of the field reserved for the interface name is insufficinet.
|
|
Solution: Printing procedure is modified to allow the interface names
|
|
of up to 16 characters (IFNAMSIZ).
|
|
Reproduction: Rename some network interface to use more characters than
|
|
the usual 4. E.g. if the original interace name is "eth1",
|
|
use this command:
|
|
ip link set dev eth1 name 123456789ABCDEF
|
|
(Note: while the maximum length of the intrface name is 16
|
|
chars, the `ip` command limits it to 15 chars.)
|
|
Issue command `qetharp -p <interface-name>`. Observe that
|
|
interface name is truncated in the output.
|
|
---
|
|
qetharp/qetharp.c | 6 +++---
|
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/qetharp/qetharp.c b/qetharp/qetharp.c
|
|
index 58debdc..5eccda3 100644
|
|
--- a/qetharp/qetharp.c
|
|
+++ b/qetharp/qetharp.c
|
|
@@ -79,7 +79,7 @@ qeth_hex_dump(unsigned char *buf, int len)
|
|
static void
|
|
show_header()
|
|
{
|
|
- printf("%-40.40s%-20.20s%-10.10s%-10.10s\n",
|
|
+ printf("%-40.40s%-20.20s%-10.10s%-16.16s\n",
|
|
"Address","HWaddress","HWType","Iface");
|
|
}
|
|
|
|
@@ -152,7 +152,7 @@ void show_entry5(__u8 ipaddr_type, __u8 *ip, struct option_info *opin)
|
|
name = fqhn;
|
|
}
|
|
}
|
|
- printf("%-40.40s%-20.20s%-10.10s%-10.10s\n", name, "","hiper",
|
|
+ printf("%-40.40s%-20.20s%-10.10s%-16.16s\n", name, "","hiper",
|
|
opin->dev_name);
|
|
}
|
|
|
|
@@ -219,7 +219,7 @@ void show_entry7(__u8 ipaddr_type, __u8 *ip, __u8 *mac,
|
|
sprintf(macstrbuf,"%02x:%02x:%02x:%02x:%02x:%02x",
|
|
mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
|
|
|
|
- printf("%-40.40s%-20.20s%-10.10s%-10.10s\n", name, macstrbuf,
|
|
+ printf("%-40.40s%-20.20s%-10.10s%-16.16s\n", name, macstrbuf,
|
|
(flags==OSACARD_FLAGS)? "ether":
|
|
(flags==OSA_TR_FLAGS)? "tr":"n/a",
|
|
opin->dev_name);
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From e7c5ab26f13a84df52c6cd772eb80d1d44c7db49 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
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 <ifname>`,
|
|
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?= <dan@danny.cz>
|
|
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 <sven[at]de.ibm.com>
|
|
-# Wolfgang Taphorn <taphorn[at]de.ibm.com>
|
|
-# Stefan Reimbold <stefan.reimbold[at]de.ibm.com>
|
|
-# Susanne Wintenberger <swinten[at]de.ibm.com>
|
|
-# Michael Mueller <mimu[at]de.ibm.com>
|
|
-#
|
|
# 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 <<EOF
|
|
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
|
-Copyright IBM Corp. 2002, 2013
|
|
+Copyright IBM Corp. 2002, 2014
|
|
EOF
|
|
}
|
|
|
|
@@ -673,7 +680,7 @@ print_alreadyrunning() {
|
|
cat <<EOF
|
|
|
|
|
|
-Please check the system if another instance of ${SCRIPTNAME} is already
|
|
+Please check the system if another instance of '${SCRIPTNAME}' is already
|
|
running. If this is not the case, please remove the lock file
|
|
'${WORKDIR_BASE}${SCRIPTNAME}.lock'.
|
|
EOF
|
|
@@ -687,22 +694,22 @@ commandline_parse()
|
|
local cmdline_arg1=${1}
|
|
local cmdline_count=${#}
|
|
|
|
- if test ${cmdline_count} -eq 1; then
|
|
- if test ${cmdline_arg1} = '-h' || test ${cmdline_arg1} = '--help'; then
|
|
+ if test "${cmdline_count}" -eq 1; then
|
|
+ if test "${cmdline_arg1}" = '-h' || test "${cmdline_arg1}" = '--help'; then
|
|
print_usage
|
|
- elif test ${cmdline_arg1} = '-v' || test ${cmdline_arg1} = '--version'; then
|
|
+ elif test "${cmdline_arg1}" = '-v' || test "${cmdline_arg1}" = '--version'; then
|
|
print_version
|
|
else
|
|
echo
|
|
- echo "${SCRIPTNAME}: invalid option ${cmdline_arg1}"
|
|
+ echo "${SCRIPTNAME}: invalid option \"${cmdline_arg1}\""
|
|
echo "Try '${SCRIPTNAME} --help' for more information"
|
|
echo
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
- elif test ${cmdline_count} -ge 1; then
|
|
+ elif test "${cmdline_count}" -ge 1; then
|
|
echo
|
|
- echo "ERROR: Invalid number of arguments!"
|
|
+ echo "${SCRIPTNAME}: Error: Invalid number of arguments!"
|
|
echo
|
|
print_usage
|
|
exit 1
|
|
@@ -717,8 +724,8 @@ environment_setup()
|
|
if test ! -e "${WORKDIR_BASE}"; then
|
|
mkdir -p "${WORKDIR_BASE}"
|
|
elif test ! -d "${WORKDIR_BASE}"; then
|
|
- echo "ERROR: ${WORKDIR_BASE} exists but this is a file!"
|
|
- echo " Please make sure ${WORKDIR_BASE} is a directory."
|
|
+ echo "${SCRIPTNAME}: Error: \"${WORKDIR_BASE}\" exists but this is a file!"
|
|
+ echo " Please make sure \"${WORKDIR_BASE}\" is a directory."
|
|
exit 1
|
|
fi
|
|
|
|
@@ -730,8 +737,8 @@ environment_setup()
|
|
fi
|
|
|
|
if ! mkdir "${WORKPATH}" 2>/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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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 <sameske@de.ibm.com>
|
|
* Gerhard Tonn <ton@de.ibm.com>
|
|
- * Copyright IBM Corp. 2001, 2006.
|
|
+ * Copyright IBM Corp. 2001, 2013.
|
|
*/
|
|
|
|
#define _LARGEFILE64_SOURCE /* needed for unistd.h */
|
|
@@ -17,6 +17,7 @@
|
|
#include <getopt.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
+#include <malloc.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
@@ -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 :"
|
|
" <not found>\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; j<info->f1c; 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; j<info->f8c; 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; j<info->f1c; 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; j<info->f8c; 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; j<info->f9c; 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 <horst.hummel@de.ibm.com>
|
|
- * Copyright IBM Corp. 2002, 2006.
|
|
+ * Copyright IBM Corp. 2002, 2013.
|
|
*/
|
|
|
|
#ifndef DASDVIEW_H
|
|
#define DASDVIEW_H
|
|
|
|
#include <limits.h>
|
|
+#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 <stdlib.h>
|
|
+#include <stdio.h>
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+#include <stdarg.h>
|
|
+
|
|
+#include <sys/stat.h>
|
|
+#include <sys/ioctl.h>
|
|
+
|
|
+
|
|
+#include "vtoc.h"
|
|
+#include "libzds.h"
|
|
+
|
|
+#include "util.h"
|
|
+
|
|
+#include <malloc.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+
|
|
+
|
|
+/** @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 <error>: <text>"
|
|
+ *
|
|
+ * @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<devices>\fR \fI<mountpoint>\fR [\fI<options>\fR]
|
|
+.SS unmounting:
|
|
+.TP
|
|
+\fBfusermount\fP -u \fI<mountpoint>\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<devices>\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<mountpoint>\fR The mount point for the specified DASD.
|
|
+.TP
|
|
+\fB\-o\fR \fI<opt>\fR,[\fI<opt>\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<device_list>\fR
|
|
+The specified file \fI<device_list>\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<n>\fR
|
|
+Size of the track buffer in tracks. The default for \fI<n>\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<n>\fR * 120KB) is allocated for the track buffer.
|
|
+
|
|
+.TP
|
|
+\fB\-o\fR seekbuffer=\fI<s>\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<n>\fR tracks, as specified
|
|
+with the `tracks' option.
|
|
+
|
|
+For small data sets and large values of \fI<n>\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<s>\fR.
|
|
+
|
|
+If \fI<s>\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<n>\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<m>\fR
|
|
+Set file permissions (octal)
|
|
+.TP
|
|
+\fB\-o\fR uid=\fI<m>\fR
|
|
+Set file owner
|
|
+.TP
|
|
+\fB\-o\fR gid=\fI<n>\fR
|
|
+Set file group
|
|
+.TP
|
|
+\fB\-o\fR max_readahead=\fI<n>\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=<data set name>,recfm=<fmt>,lrecl=<size>,dsorg=<org>
|
|
+
|
|
+\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 <fuse.h>
|
|
+#include <fuse_opt.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <stddef.h>
|
|
+#include <stdlib.h>
|
|
+#include <limits.h>
|
|
+#include <malloc.h>
|
|
+#include <pthread.h>
|
|
+
|
|
+#ifdef HAVE_SETXATTR
|
|
+#include <linux/xattr.h>
|
|
+#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 <devices> <mountpoint> [<options>]\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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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 <rwuerthn@de.ibm.com> and
|
|
-Felix Beck <felix.beck@de.ibm.com>.
|
|
-.fi
|
|
--
|
|
1.9.3
|
|
|
|
|
|
From 259ad6e825da6a18ef7c1a7ba1117c3a39d09736 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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 <<EOF
|
|
+${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
|
+Copyright IBM Corp. 2002, 2014
|
|
+EOF
|
|
+}
|
|
+
|
|
+
|
|
+########################################
|
|
+# print how to use this script
|
|
+print_usage()
|
|
+{
|
|
+ print_version
|
|
+
|
|
+ cat <<EOF
|
|
+
|
|
+
|
|
+Usage: ${SCRIPTNAME} [OPTIONS]
|
|
+
|
|
+This script collects runtime, configuration and trace information about
|
|
+your Linux on System z installation for debugging purposes.
|
|
+
|
|
+It also traces information about z/VM if the Linux runs under z/VM.
|
|
+
|
|
+
|
|
+The collected information is written to a TAR archive named
|
|
+
|
|
+ /tmp/DBGINFO-[date]-[time]-[hostname]-[processorid].tgz
|
|
+
|
|
+where [date] and [time] are the date and time when debug data is collected.
|
|
+[hostname] indicates the hostname of the system the data was collected from.
|
|
+The [processorid] is taken from the processor 0 and indicates the processor
|
|
+identification.
|
|
+
|
|
+Options:
|
|
+
|
|
+ -d|--directory specify the directory where the data collection
|
|
+ stores the temporary data and the final archive.
|
|
+ -h|--help print this help
|
|
+ -v|--version print version information
|
|
+
|
|
+
|
|
+Please report bugs to: linux390@de.ibm.com
|
|
+
|
|
+EOF
|
|
+}
|
|
+
|
|
+######################################
|
|
+# 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
|
|
+
|
|
+#######################################
|
|
+# 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 <<EOF
|
|
-${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
|
-Copyright IBM Corp. 2002, 2014
|
|
-EOF
|
|
-}
|
|
-
|
|
-
|
|
-########################################
|
|
-# print how to use this script
|
|
-print_usage()
|
|
-{
|
|
- print_version
|
|
-
|
|
- cat <<EOF
|
|
-
|
|
-
|
|
-Usage: ${SCRIPTNAME} [OPTIONS]
|
|
-
|
|
-This script collects runtime, configuration and trace information about
|
|
-your Linux on System z installation for debugging purposes.
|
|
-
|
|
-It also traces information about z/VM if the Linux runs under z/VM.
|
|
-
|
|
-
|
|
-The collected information is written to a TAR archive named
|
|
-
|
|
- /tmp/DBGINFO-[date]-[time]-[hostname]-[processorid].tgz
|
|
-
|
|
-where [date] and [time] are the date and time when debug data is collected.
|
|
-[hostname] indicates the hostname of the system the data was collected from.
|
|
-The [processorid] is taken from the processor 0 and indicates the processor
|
|
-identification.
|
|
-
|
|
-Options:
|
|
-
|
|
- -h|--help print this help
|
|
- -v|--version print version information
|
|
-
|
|
-
|
|
-Please report bugs to: linux390@de.ibm.com
|
|
-
|
|
-EOF
|
|
-}
|
|
-
|
|
-
|
|
-########################################
|
|
# print that an instance is already running
|
|
print_alreadyrunning() {
|
|
print_version
|
|
@@ -682,42 +737,12 @@ print_alreadyrunning() {
|
|
|
|
Please check the system if another instance of '${SCRIPTNAME}' is already
|
|
running. If this is not the case, please remove the lock file
|
|
-'${WORKDIR_BASE}${SCRIPTNAME}.lock'.
|
|
+'${LOCKFILE}'.
|
|
EOF
|
|
}
|
|
|
|
|
|
########################################
|
|
-#
|
|
-commandline_parse()
|
|
-{
|
|
- local cmdline_arg1=${1}
|
|
- local cmdline_count=${#}
|
|
-
|
|
- if test "${cmdline_count}" -eq 1; then
|
|
- if test "${cmdline_arg1}" = '-h' || test "${cmdline_arg1}" = '--help'; then
|
|
- print_usage
|
|
- elif test "${cmdline_arg1}" = '-v' || test "${cmdline_arg1}" = '--version'; then
|
|
- print_version
|
|
- else
|
|
- echo
|
|
- echo "${SCRIPTNAME}: invalid option \"${cmdline_arg1}\""
|
|
- echo "Try '${SCRIPTNAME} --help' for more information"
|
|
- echo
|
|
- exit 1
|
|
- fi
|
|
- exit 0
|
|
- elif test "${cmdline_count}" -ge 1; then
|
|
- echo
|
|
- echo "${SCRIPTNAME}: Error: Invalid number of arguments!"
|
|
- echo
|
|
- print_usage
|
|
- exit 1
|
|
- fi
|
|
-}
|
|
-
|
|
-
|
|
-########################################
|
|
# Setup the environment
|
|
environment_setup()
|
|
{
|
|
@@ -729,11 +754,12 @@ environment_setup()
|
|
exit 1
|
|
fi
|
|
|
|
- if test -e "${WORKDIR_BASE}${SCRIPTNAME}".lock; then
|
|
+ if test -e "${LOCKFILE}"; then
|
|
print_alreadyrunning
|
|
exit 1
|
|
else
|
|
- touch "${WORKDIR_BASE}${SCRIPTNAME}".lock
|
|
+ touch "${LOCKFILE}"
|
|
+ echo "${DATETIME}" > "${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 <DIRECTORY>\fP, \fB\-\-directory <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?= <dan@danny.cz>
|
|
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?= <dan@danny.cz>
|
|
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 <setjmp.h>
|
|
|
|
#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
|
|
|