Subject: zdsfs: add online vtoc refresh
From: Stefan Haberland <sth@linux.ibm.com>

Summary:     zdsfs: add online vtoc refresh 
Description: Enable zdsfs to access datasets that were created after zdsfs was
             mounted without the need to remount zdsfs.
             This is done by re-reading the VTOC with every readdir system
             call.
             To ensure a consistent VTOC state the DASD device is reserved for
             every VTOC read and released afterwards.
Upstream-ID: bc053a975af6bd8eecd31269656f305d4c744fbe
Problem-ID:  IO1808

Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
---
 dasdview/dasdview.c  |    2 
 include/lib/libzds.h |   86 +++++++++++++++--
 libzds/libzds.c      |  249 +++++++++++++++++++++++++++++----------------------
 zdsfs/zdsfs.c        |   97 ++++++++++++++++---
 4 files changed, 303 insertions(+), 131 deletions(-)

--- a/dasdview/dasdview.c
+++ b/dasdview/dasdview.c
@@ -1793,7 +1793,7 @@ static void dasdview_print_vtoc_raw(dasd
 			       " rc=%d\n", rc);
 		exit(-1);
 	}
-	rc = lzds_dasd_read_rawvtoc(info->dasd);
+	rc = lzds_dasd_alloc_rawvtoc(info->dasd);
 	if (rc == EINVAL) {
 		zt_error_print("dasdview: Cannot read VTOC because disk does"
 			       " not contain valid VOL1 label.\n",
--- a/include/lib/libzds.h
+++ b/include/lib/libzds.h
@@ -112,6 +112,8 @@
  */
 #define LIB_LIBZDS_H
 
+#include "lib/util_base.h"
+#include "lib/util_list.h"
 #include "vtoc.h"
 
 
@@ -329,10 +331,73 @@ struct pds_member_entry {
 struct zdsroot;
 
 /**
+ * @struct raw_vtoc
+ * @brief The VTOC is a directory of data sets on one DASD
+ *
+ * As the VTOC is the data area on the DASD that describes all data sets,
+ * this library will often have to refer to the various records in the VTOC.
+ * To make this more efficient, we will read the whole VTOC once and identify
+ * all elements (DSCBs). The raw data of the VTOC tracks and the index to the
+ * DSCBs is stored.
+ */
+struct raw_vtoc {
+	/** @brief The raw track data  */
+	char *rawdata;
+	/** @brief This size of the raw track data in bytes */
+	unsigned long long rawdatasize;
+	/** @brief An array with pointers to the various DSCBs in the rawdata */
+	char **vtocindex;
+	/** @brief Number of entries in the index */
+	unsigned int vtocindexcount;
+	/** @brief Number of records per VTOC track
+	 *
+	 *  @note While the DS4DEVDT field in the format 4 DSCB names the number
+	 *  if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1
+	 *  for record 0.
+	 */
+	unsigned int vtoc_rec_per_track;
+	/** @brief The track number at which the vtoc begins on the DASD */
+	unsigned int vtoctrackoffset;
+	/** @brief Start record of VTOC.
+	 *
+	 *  The rawdata contains full tracks. This is the number of the first
+	 *  record that actually belongs to the VTOC
+	 */
+	unsigned int vtocrecno;
+	/** @brief The DASD this vtoc was read from */
+	struct dasd *dasd;
+	/** @brief Detailed error messages in case of a problem */
+	struct errorlog *log;
+};
+
+/**
  * @struct dasd
  * @brief Represents one physical device, may have a vtoc
  */
-struct dasd;
+struct dasd {
+	/** @brief List head used to store a list of DASDs in struct zdsroot */
+	struct util_list_node list;
+	/** @brief Name of the block device, e.g. /dev/dasde */
+	char *device;
+	/** @brief File descriptor for the block device.
+	 *
+	 * The device is kept open for as along as the library uses it.
+	 * This lets the system know that the device is still in use.
+	 */
+	int inusefd;
+	/* @brief where to find the volume label */
+	unsigned int label_block;
+	/** @brief Device geometry. How many cylinders does the DASD have. */
+	unsigned int cylinders;
+	/** @brief Device geometry. How many heads does the DASD have. */
+	unsigned int heads;
+	/** @brief The VTOC data that has been read from this device */
+	struct raw_vtoc *rawvtoc;
+	/** @brief The volume label that has been read from this device */
+	volume_label_t *vlabel;
+	/** @brief Detailed error messages in case of a problem */
+	struct errorlog *log;
+};
 
 /**
  * @struct dasditerator
@@ -351,12 +416,6 @@ struct dasditerator;
 struct dasdhandle;
 
 /**
- * @struct raw_vtoc
- * @brief The VTOC is a directory of data sets on one dasd
- */
-struct raw_vtoc;
-
-/**
  * @struct dscbiterator
  * @brief  allows to iterate over all DSCBs in a vtoc
  */
@@ -572,7 +631,13 @@ int lzds_dasd_get_vlabel(struct dasd *da
  * @brief Read the vtoc data from device. The data as stored as part
  *        of the struct dasd.
  */
-int lzds_dasd_read_rawvtoc(struct dasd *dasd);
+int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *vtoc);
+
+/**
+ * @brief Read the vtoc data from device. The data as stored as part
+ *        of the struct dasd.
+ */
+int lzds_dasd_alloc_rawvtoc(struct dasd *dasd);
 
 /**
  * @brief Get the previously read raw_vtoc data.
@@ -787,6 +852,8 @@ int lzds_zdsroot_extract_datasets_from_d
 					    struct dasd *dasd);
 
 
+void lzds_dslist_free(struct zdsroot *root);
+
 
 /** @} */ /* end of group libzds_functions_high */
 
@@ -808,6 +875,9 @@ void lzds_DS1RECFM_to_recfm(char DS1RECF
 
 int lzds_analyse_open_count(struct zdsroot *root, int warn);
 
+int dasd_disk_reserve(struct dasd *dasd);
+int dasd_disk_release(struct dasd *dasd);
+
 /** @} */ /* end of group libzds_functions_helper */
 
 
--- a/libzds/libzds.c
+++ b/libzds/libzds.c
@@ -25,8 +25,6 @@
 
 #include "lib/libzds.h"
 #include "lib/u2s.h"
-#include "lib/util_base.h"
-#include "lib/util_list.h"
 #include "lib/vtoc.h"
 
 /** @cond PRIVATE */
@@ -38,6 +36,11 @@
 /* device size in bytes (u64 *arg) */
 #define BLKGETSIZE64 _IOR(0x12, 114, size_t)
 
+#define DASD_IOCTL_LETTER 'D'
+/* Reserve the device for the current LPAR */
+#define BIODASDRSRV    _IO(DASD_IOCTL_LETTER, 2)
+/* Release the device for the current LPAR */
+#define BIODASDRLSE    _IO(DASD_IOCTL_LETTER, 3)
 
 /******************************************************************************/
 /*     libzds structure definitions					      */
@@ -80,42 +83,6 @@ struct errormsg {
 	char text[ERRORMSG];
 };
 
-/**
- * As the VTOC is the data area on the DASD that describes all data sets,
- * this library will often have to refer to the various records in the VTOC.
- * To make this more efficiant, we will read the whole VTOC once and identify
- * all elements (DSCBs). The raw data of the VTOC tracks and the index to the
- * DSCBs is stored.
- */
-struct raw_vtoc {
-	/** @brief The raw track data  */
-	char *rawdata;
-	/** @brief This size of the raw track data in bytes */
-	unsigned long long rawdatasize;
-	/** @brief An array with pointers to the various DSCBs in the rawdata */
-	char **vtocindex;
-	/** @brief Number of entries in the index */
-	unsigned int vtocindexcount;
-	/** @brief Number of records per VTOC track
-	 *
-	 *  @note While the DS4DEVDT field in the format 4 DSCB names the number
-	 *  if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1
-	 *  for record 0.
-	 */
-	unsigned int vtoc_rec_per_track;
-	/** @brief The track number in which the vtoc begins on the DASD */
-	unsigned int vtoctrackoffset;
-	/** @brief Start record of VTOC.
-	 *
-	 *  The rawdata contains full tracks. This is the number of the first
-	 *  record that actually belongs to the VTOC */
-	unsigned int vtocrecno;
-	/** @brief The DASD this vtoc was read from */
-	struct dasd *dasd;
-	/** @brief Detailed error messages in case of a problem */
-	struct errorlog *log;
-};
-
 struct dscbiterator {
 	/** @brief The raw_vtoc this iterator refers to */
 	struct raw_vtoc *rawvtoc;
@@ -123,31 +90,6 @@ struct dscbiterator {
 	unsigned int i;
 };
 
-struct dasd {
-	/** @brief List head used to store a list of DASDs in struct zdsroot */
-	struct util_list_node list;
-	/** @brief Name of the block device, e.g. /dev/dasde */
-	char *device;
-	/** @brief File descriptor for the block device.
-	 *
-	 * The device is kept open for as along as the library uses it.
-	 * This lets the system know that the device is still in use.
-	 */
-	int inusefd;
-	/* @brief where to find the volume label */
-	unsigned int label_block;
-	/** @brief Device geometry. How many cylinders does the DASD have. */
-	unsigned int cylinders;
-	/** @brief Device geometry. How many heads does the DASD have. */
-	unsigned int heads;
-	/** @brief The VTOC data that has been read from this device */
-	struct raw_vtoc *rawvtoc;
-	/** @brief The volume label that has been read from this device */
-	volume_label_t *vlabel;
-	/** @brief Detailed error messages in case of a problem */
-	struct errorlog *log;
-};
-
 struct dasdhandle {
 	/** @brief The struct dasd this context relates to */
 	struct dasd *dasd;
@@ -432,6 +374,7 @@ int lzds_zdsroot_alloc(struct zdsroot **
 	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
@@ -439,21 +382,11 @@ int lzds_zdsroot_alloc(struct zdsroot **
  *
  * @param[in] root Reference to the zdsroot structure that is to be freed.
  */
-void lzds_zdsroot_free(struct zdsroot *root)
+void lzds_dslist_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);
@@ -462,6 +395,28 @@ void lzds_zdsroot_free(struct zdsroot *r
 		errorlog_free(ds->log);
 		free(ds);
 	}
+}
+
+/**
+ * It should be noted that this frees all structures that are owned by the
+ * root structure as well. For example, a pointer to a struct dasd that
+ * 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;
+
+	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);
+	lzds_dslist_free(root);
 	util_list_free(root->datasetlist);
 	errorlog_free(root->log);
 	free(root);
@@ -509,6 +464,66 @@ static int dasd_read_geometry(struct das
 	return 0;
 }
 
+/**
+ * @brief Reserve DASD disk.
+ *
+ * @param[in] dasd Reference to the dasd to work on.
+ *
+ * @return 0		in case of success
+ *   - errno		in case of failure
+ *
+ */
+int dasd_disk_reserve(struct dasd *dasd)
+{
+	int fd;
+
+	errorlog_clear(dasd->log);
+	fd = open(dasd->device, O_RDONLY);
+	if (fd < 0)
+		return errorlog_add_message(
+			&dasd->log, NULL, EIO,
+			"reserve: could not open device %s\n",
+			dasd->device);
+
+	if (ioctl(fd, BIODASDRSRV, NULL) != 0)
+		return errorlog_add_message(
+			&dasd->log, NULL, EIO,
+			"reserve: could not reserve device %s\n",
+			dasd->device);
+
+	close(fd);
+	return 0;
+}
+
+/**
+ * @brief Release DASD disk
+ *
+ * @param[in] dasd Reference to the dasd to work on.
+ *
+ * @return 0		in case of success
+ *   - errno		in case of failure
+ */
+int dasd_disk_release(struct dasd *dasd)
+{
+	int fd;
+
+	errorlog_clear(dasd->log);
+	fd = open(dasd->device, O_RDONLY);
+	if (fd < 0)
+		return errorlog_add_message(
+			&dasd->log, NULL, EIO,
+			"release: could not open device %s\n",
+			dasd->device);
+
+	if (ioctl(fd, BIODASDRLSE, NULL) != 0)
+		return errorlog_add_message(
+			&dasd->log, NULL, EIO,
+			"release: could not release device %s\n",
+			dasd->device);
+
+	close(fd);
+	return 0;
+}
 
 /**
  * @brief Subroutine of lzds_zdsroot_add_device
@@ -1475,7 +1490,7 @@ int lzds_raw_vtoc_get_dscb_from_cchhb(st
  *   - EPROTO  The VTOC data is not in a valid format.
  *   - EIO     Other I/O error
  */
-int lzds_dasd_read_rawvtoc(struct dasd *dasd)
+int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc)
 {
 	unsigned long long vtoctrckno, vtocrecno;
 	unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize;
@@ -1487,26 +1502,11 @@ int lzds_dasd_read_rawvtoc(struct dasd *
 	format4_label_t *f4;
 	unsigned long long rawvtocsize;
 
-	struct raw_vtoc *rawvtoc = NULL;
 	volume_label_t *vlabel = NULL;
 	char *trackdata = NULL;
 	char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00}; /* "VOL1" in EBCDIC */
 
 	errorlog_clear(dasd->log);
-	/* cleanup the old rawvtoc structures before we read new ones */
-	rawvtoc = dasd->rawvtoc;
-	dasd->rawvtoc = NULL;
-	if (rawvtoc) {
-		free(rawvtoc->rawdata);
-		free(rawvtoc->vtocindex);
-		free(rawvtoc);
-	}
-
-	rawvtoc = malloc(sizeof(*rawvtoc));
-	if (!rawvtoc)
-		return ENOMEM;
-	memset(rawvtoc, 0, sizeof(*rawvtoc));
-	rawvtoc->dasd = dasd;
 
 	rc = lzds_dasd_get_vlabel(dasd, &vlabel);
 	if (rc) {
@@ -1643,13 +1643,49 @@ int lzds_dasd_read_rawvtoc(struct dasd *
 		++i;
 	}
 
-	dasd->rawvtoc = rawvtoc;
 	return 0;
 
 cleanup:
-	free(rawvtoc->vtocindex);
 	free(trackdata);
-	free(rawvtoc);
+	return rc;
+}
+
+/**
+ * @param[in]  dasd The struct dasd that represents the device we want to read
+ *                  the VTOC from.
+ * @return     0 on success, otherwise one of the following error codes:
+ *   - ENOMEM  Could not allocate internal structure due to lack of memory.
+ *   - EINVAL  The volume label has not yet been read or it is not valid.
+ *   - EPROTO  The VTOC data is not in a valid format.
+ *   - EIO     Other I/O error
+ */
+int lzds_dasd_alloc_rawvtoc(struct dasd *dasd)
+{
+	struct raw_vtoc *rawvtoc = NULL;
+	int rc;
+
+	/* cleanup the old rawvtoc structures before we read new ones */
+	rawvtoc = dasd->rawvtoc;
+	dasd->rawvtoc = NULL;
+	if (rawvtoc) {
+		free(rawvtoc->rawdata);
+		free(rawvtoc->vtocindex);
+		free(rawvtoc);
+	}
+
+	rawvtoc = malloc(sizeof(*rawvtoc));
+	if (!rawvtoc)
+		return ENOMEM;
+	memset(rawvtoc, 0, sizeof(*rawvtoc));
+	rawvtoc->dasd = dasd;
+
+	rc = lzds_dasd_read_rawvtoc(dasd, rawvtoc);
+	if (rc) {
+		free(rawvtoc->vtocindex);
+		free(rawvtoc);
+	} else {
+		dasd->rawvtoc = rawvtoc;
+	}
 	return rc;
 }
 
@@ -2295,6 +2331,7 @@ out1:
 static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds)
 {
 	int k, l, dspcount;
+
 	for (k = 0; k < MAXVOLUMESPERDS; ++k) {
 		/* if both datasets have a part in position k,
 		 * then something is wrong */
@@ -2314,18 +2351,20 @@ static int dataset_merge_dataset(struct
 			 * Since dsp[0] may not be set yet, we loop over the
 			 * base dsp array until we find an entry.
 			 */
-			for (l = 0; l < MAXVOLUMESPERDS; ++l)
-				if (baseds->dsp[l]) {
-					if (memcmp(baseds->dsp[l]->f1->DS1DSSN,
-						   newds->dsp[k]->f1->DS1DSSN,
-						   MAXVOLSER))
-						return errorlog_add_message(
-						     &baseds->log, NULL, EPROTO,
-				       "merge dataset: part %d has incompatible"
-						    " base volume serial\n", k);
-					else
-						break;
-				}
+			for (l = 0; l < MAXVOLUMESPERDS; ++l) {
+				if (!baseds->dsp[l])
+					continue;
+				if (memcmp(baseds->dsp[l]->f1->DS1DSSN,
+					   newds->dsp[k]->f1->DS1DSSN,
+					   MAXVOLSER))
+					return errorlog_add_message(
+						&baseds->log, NULL, EPROTO,
+						"merge dataset: part %d has incompatible base volume serial\n",
+						k);
+				else
+					break;
+			}
+
 			baseds->dsp[k] = newds->dsp[k];
 			baseds->dspcount++;
 
--- a/zdsfs/zdsfs.c
+++ b/zdsfs/zdsfs.c
@@ -52,6 +52,8 @@ struct zdsfs_info {
 };
 
 static struct zdsfs_info zdsfsinfo;
+static int zdsfs_create_meta_data_buffer(struct zdsfs_info *);
+static int zdsfs_verify_datasets(void);
 
 struct zdsfs_file_info {
 	struct dshandle *dsh;
@@ -194,6 +196,48 @@ static int zdsfs_getattr(const char *pat
 	return 0;
 }
 
+static void zdsfs_read_device(struct dasd *newdasd, const char *device)
+{
+	struct errorlog *log;
+	int rc;
+
+	rc = dasd_disk_reserve(newdasd);
+	if (rc) {
+		fprintf(stderr, "error when reserving device %s: %s\n",
+			device, strerror(rc));
+		lzds_dasd_get_errorlog(newdasd, &log);
+		lzds_errorlog_fprint(log, stderr);
+		exit(1);
+	}
+	rc = lzds_dasd_alloc_rawvtoc(newdasd);
+	if (rc) {
+		fprintf(stderr, "error when reading VTOC from device %s: %s\n",
+			device, strerror(rc));
+		lzds_dasd_get_errorlog(newdasd, &log);
+		lzds_errorlog_fprint(log, stderr);
+		exit(1);
+	}
+	rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot,
+						     newdasd);
+	if (rc) {
+		fprintf(stderr,
+			"error when extracting data sets from dasd %s: %s\n",
+			device, strerror(rc));
+		lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log);
+		lzds_errorlog_fprint(log, stderr);
+		exit(1);
+	}
+	rc = dasd_disk_release(newdasd);
+	if (rc) {
+		fprintf(stderr, "error when releasing device %s: %s\n",
+			device, strerror(rc));
+		lzds_dasd_get_errorlog(newdasd, &log);
+		lzds_errorlog_fprint(log, stderr);
+		exit(1);
+	}
+}
+
+
 static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs)
 {
 	struct dasditerator *dasdit;
@@ -240,6 +284,33 @@ static int zdsfs_statfs(const char *UNUS
 	return 0;
 }
 
+
+static int zdsfs_update_vtoc(void)
+{
+	struct dasditerator *dasdit;
+	struct dasd *dasd;
+	int rc;
+
+	lzds_dslist_free(zdsfsinfo.zdsroot);
+	rc = lzds_zdsroot_alloc_dasditerator(zdsfsinfo.zdsroot, &dasdit);
+	if (rc)
+		return -ENOMEM;
+
+	while (!lzds_dasditerator_get_next_dasd(dasdit, &dasd))
+		zdsfs_read_device(dasd, dasd->device);
+
+	lzds_dasditerator_free(dasdit);
+	rc = zdsfs_verify_datasets();
+	if (rc)
+		return rc;
+
+	rc = zdsfs_create_meta_data_buffer(&zdsfsinfo);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
 static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
 			 off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi))
 {
@@ -253,6 +324,10 @@ static int zdsfs_readdir(const char *pat
 	int rc;
 	int ispds, issupported;
 
+	rc = zdsfs_update_vtoc();
+	if (rc)
+		return rc;
+
 	/* we have two type of directories
 	 * type one: the root directory contains all data sets
 	 */
@@ -329,6 +404,9 @@ static int zdsfs_open(const char *path,
 		goto error1;
 
 	if (strcmp(path, "/"METADATAFILE) == 0) {
+		rc = zdsfs_update_vtoc();
+		if (rc)
+			return rc;
 		zfi->dsh = NULL;
 		zfi->is_metadata_file = 1;
 		zfi->metaread = 0;
@@ -596,6 +674,7 @@ static int zdsfs_verify_datasets(void)
 	if (rc)
 		return ENOMEM;
 	while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) {
+		lzds_dataset_get_name(ds, &dsname);
 		lzds_dataset_get_is_complete(ds, &iscomplete);
 		if (!iscomplete) {
 			lzds_dataset_get_name(ds, &dsname);
@@ -822,23 +901,7 @@ static void zdsfs_process_device(const c
 		lzds_errorlog_fprint(log, stderr);
 		exit(1);
 	}
-	rc = lzds_dasd_read_rawvtoc(newdasd);
-	if (rc) {
-		fprintf(stderr, "error when reading VTOC from device %s:"
-			" %s\n", device, strerror(rc));
-		lzds_dasd_get_errorlog(newdasd, &log);
-		lzds_errorlog_fprint(log, stderr);
-		exit(1);
-	}
-	rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot,
-						     newdasd);
-	if (rc) {
-		fprintf(stderr, "error when extracting data sets from dasd %s:"
-			" %s\n", device, strerror(rc));
-		lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log);
-		lzds_errorlog_fprint(log, stderr);
-		exit(1);
-	}
+	zdsfs_read_device(newdasd, device);
 }
 
 static void zdsfs_process_device_file(const char *devfile)
