/**
 * \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 Corp. 2013, 2017
 *
 * s390-tools is free software; you can redistribute it and/or modify
 * it under the terms of the MIT license. See LICENSE for details.
 */

/**
 * @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 divided 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 divided 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 LIB_LIBZDS_H
/**
 * @brief Watchdog for libzds.h inclusion.
 */
#define LIB_LIBZDS_H

#include "vtoc.h"



/**
 *  \defgroup external_interfaces  External constants and structures.
 *  @{
 *  @brief These constants and structures are related to
 *         hardware and software 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 useful helpers.
 */

/**
 * @brief Translates a DS1RECFM byte to a recfm format string.
 */
void lzds_DS1RECFM_to_recfm(char DS1RECFM, char *buffer);


int lzds_analyse_open_count(struct zdsroot *root, int warn);

/** @} */ /* end of group libzds_functions_helper */




#endif /* LIB_LIBZDS_H */
