mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
zdsfs: coordinated read access
For data consistency it is essential that datasets are not modified by z/OS applications during zdsfs access. This is currently ensured by manually setting the device offline in z/OS before mounting it in Linux. This patch improves the usability and data security by making this manual step obsolete. Before opening a dataset zdsfs will obtain an ENQ to mark it as in use for z/OS and release the ENQ when closing the dataset. A timer is set up that pings the z/OSMF REST services with the ENQ to prevent it from a timeout after 10 minutes. The behavior of zdsfs can be configured in a new config file or with new command line options. Signed-off-by: Stefan Haberland <sth@linux.ibm.com> Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
b044aec4ff
commit
5737a57920
@@ -192,7 +192,7 @@
|
||||
|
||||
|
||||
#define MAX_LINE_LENGTH 512
|
||||
|
||||
#define MAX_SERVER 3
|
||||
|
||||
/**
|
||||
* @brief This structure represents the count field in an ECKD record.
|
||||
|
||||
284
zdsfs/zdsfs.c
284
zdsfs/zdsfs.c
@@ -21,6 +21,9 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef HAVE_SETXATTR
|
||||
#include <linux/xattr.h>
|
||||
@@ -36,6 +39,8 @@
|
||||
/* defaults for file and directory permissions (octal) */
|
||||
#define DEF_FILE_PERM 0440
|
||||
#define DEF_DIR_PERM 0550
|
||||
/* default timer interval 9 minutes, enq times out after 10 minutes */
|
||||
#define DEFAULT_KEEPALIVE_SEC 540
|
||||
|
||||
struct zdsfs_info {
|
||||
int devcount;
|
||||
@@ -50,11 +55,24 @@ struct zdsfs_info {
|
||||
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 */
|
||||
char *configfile;
|
||||
int restapi;
|
||||
unsigned int nr_server;
|
||||
int active_server;
|
||||
char *server[MAX_SERVER];
|
||||
long keepalive;
|
||||
};
|
||||
|
||||
static struct zdsfs_info zdsfsinfo;
|
||||
static int zdsfs_create_meta_data_buffer(struct zdsfs_info *);
|
||||
static int zdsfs_verify_datasets(void);
|
||||
static struct util_list *open_dsh;
|
||||
static int timer_running;
|
||||
|
||||
struct dsh_node {
|
||||
struct util_list_node node;
|
||||
struct dshandle *dsh;
|
||||
};
|
||||
|
||||
struct zdsfs_file_info {
|
||||
struct dshandle *dsh;
|
||||
@@ -64,6 +82,67 @@ struct zdsfs_file_info {
|
||||
size_t metaread; /* how many bytes have already been read */
|
||||
};
|
||||
|
||||
/* Allocate and initialize a new list of struct dsh_node. */
|
||||
static struct util_list *dshlist_alloc(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
|
||||
list = util_malloc(sizeof(struct util_list));
|
||||
util_list_init(list, struct dsh_node, node);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* free list of struct dsh_node. */
|
||||
static void dshlist_free(struct util_list *list)
|
||||
{
|
||||
struct dsh_node *s, *n;
|
||||
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
util_list_iterate_safe(list, s, n) {
|
||||
util_list_remove(list, s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* add dsh to list */
|
||||
static void dshlist_add(struct util_list *list, struct dshandle *dsh)
|
||||
{
|
||||
struct dsh_node *s;
|
||||
|
||||
s = util_malloc(sizeof(struct dsh_node));
|
||||
s->dsh = dsh;
|
||||
util_list_add_tail(list, s);
|
||||
}
|
||||
|
||||
/* Find a dsh_node. */
|
||||
static struct dsh_node *dshlist_find(struct util_list *list, struct dshandle *dsh)
|
||||
{
|
||||
struct dsh_node *s;
|
||||
|
||||
util_list_iterate(list, s) {
|
||||
if (s->dsh == dsh)
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove a dsh_node from the list. */
|
||||
void dshlist_remove(struct util_list *list, struct dshandle *dsh)
|
||||
{
|
||||
struct dsh_node *p;
|
||||
|
||||
p = dshlist_find(list, dsh);
|
||||
if (p) {
|
||||
util_list_remove(list, p);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* normalize the given path name to a dataset name
|
||||
@@ -97,7 +176,47 @@ static void path_to_member_name(const char *path, char *normds, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_timer(long sec)
|
||||
{
|
||||
static struct itimerval timer;
|
||||
|
||||
memset(&timer, 0, sizeof(struct itimerval));
|
||||
timer.it_value.tv_sec = sec;
|
||||
setitimer(ITIMER_REAL, &timer, NULL);
|
||||
}
|
||||
|
||||
static void keep_alive(int UNUSED(signal))
|
||||
{
|
||||
struct dsh_node *s;
|
||||
|
||||
if (!open_dsh) {
|
||||
timer_running = 0;
|
||||
setup_timer(0);
|
||||
return;
|
||||
}
|
||||
|
||||
util_list_iterate(open_dsh, s) {
|
||||
lzds_rest_ping(s->dsh,
|
||||
zdsfsinfo.server[zdsfsinfo.active_server]);
|
||||
}
|
||||
timer_running = 1;
|
||||
setup_timer(zdsfsinfo.keepalive);
|
||||
}
|
||||
|
||||
void keepalive_start(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
if (!open_dsh || timer_running)
|
||||
return;
|
||||
|
||||
timer_running = 1;
|
||||
/* Setup timer for periodic ping. */
|
||||
memset(&act, 0, sizeof(struct sigaction));
|
||||
act.sa_handler = &keep_alive;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
setup_timer(zdsfsinfo.keepalive);
|
||||
}
|
||||
|
||||
static int zdsfs_getattr(const char *path, struct stat *stbuf)
|
||||
{
|
||||
@@ -375,6 +494,43 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* walk through the serverlist and check if the URLs start with http or https
|
||||
* if not attach a https:// prefix
|
||||
* also check if they end with / and if not attach it
|
||||
*
|
||||
* afterwards ping the z/OSMF server and use the first working one
|
||||
*
|
||||
* return 1 if working server found 0 otherwise
|
||||
*/
|
||||
static int zdsfs_test_restserver(void)
|
||||
{
|
||||
unsigned int i;
|
||||
char *server;
|
||||
char *prefix;
|
||||
|
||||
for (i = 0; i < zdsfsinfo.nr_server; i++) {
|
||||
server = zdsfsinfo.server[i];
|
||||
if (strncmp(server, "http", 4)) {
|
||||
prefix = util_strdup("https://");
|
||||
server = util_strcat_realloc(prefix, server);
|
||||
free(zdsfsinfo.server[i]);
|
||||
zdsfsinfo.server[i] = server;
|
||||
}
|
||||
if (strncmp(server + strlen(server) - 1, "/", 1)) {
|
||||
server = util_strcat_realloc(server, "/");
|
||||
zdsfsinfo.server[i] = server;
|
||||
}
|
||||
if (lzds_rest_ping(NULL, zdsfsinfo.server[i])) {
|
||||
zdsfsinfo.active_server = i;
|
||||
fprintf(stdout, "Using z/OSMF REST services on %s\n",
|
||||
zdsfsinfo.server[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int zdsfs_open(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
@@ -464,13 +620,32 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi)
|
||||
rc = -rc;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
retry:
|
||||
if (zdsfsinfo.restapi && zdsfsinfo.active_server >= 0) {
|
||||
rc = lzds_rest_get_enq(dsh,
|
||||
zdsfsinfo.server[zdsfsinfo.active_server]);
|
||||
/* if the REST server is not responding try the other */
|
||||
if (rc == ECONNREFUSED && zdsfs_test_restserver()) {
|
||||
goto retry;
|
||||
} else if (rc) {
|
||||
lzds_dshandle_get_errorlog(dsh, &log);
|
||||
lzds_errorlog_fprint(log, stderr);
|
||||
rc = -rc;
|
||||
goto error2;
|
||||
} else {
|
||||
dshlist_add(open_dsh, dsh);
|
||||
/* add to open dsh list */
|
||||
keepalive_start();
|
||||
}
|
||||
}
|
||||
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;
|
||||
goto error3;
|
||||
}
|
||||
zfi->is_metadata_file = 0;
|
||||
zfi->metaread = 0;
|
||||
@@ -478,6 +653,8 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi)
|
||||
fi->fh = (uint64_t)(unsigned long)zfi;
|
||||
return 0;
|
||||
|
||||
error3:
|
||||
dshlist_remove(open_dsh, dsh);
|
||||
error2:
|
||||
lzds_dshandle_free(dsh);
|
||||
error1:
|
||||
@@ -495,7 +672,10 @@ static int zdsfs_release(const char *UNUSED(path), struct fuse_file_info *fi)
|
||||
return -EINVAL;
|
||||
zfi = (struct zdsfs_file_info *)(unsigned long)fi->fh;
|
||||
if (zfi->dsh) {
|
||||
lzds_rest_release_enq(zfi->dsh,
|
||||
zdsfsinfo.server[zdsfsinfo.active_server]);
|
||||
lzds_dshandle_close(zfi->dsh);
|
||||
dshlist_remove(open_dsh, zfi->dsh);
|
||||
lzds_dshandle_free(zfi->dsh);
|
||||
}
|
||||
rc = pthread_mutex_destroy(&zfi->mutex);
|
||||
@@ -831,6 +1011,8 @@ enum {
|
||||
KEY_DEVFILE,
|
||||
KEY_TRACKS,
|
||||
KEY_SEEKBUFFER,
|
||||
KEY_CONFIG,
|
||||
KEY_SERVER,
|
||||
};
|
||||
|
||||
#define ZDSFS_OPT(t, p, v) { t, offsetof(struct zdsfs_info, p), v }
|
||||
@@ -843,9 +1025,12 @@ static const struct fuse_opt zdsfs_opts[] = {
|
||||
FUSE_OPT_KEY("-l %s", KEY_DEVFILE),
|
||||
FUSE_OPT_KEY("tracks=", KEY_TRACKS),
|
||||
FUSE_OPT_KEY("seekbuffer=", KEY_SEEKBUFFER),
|
||||
FUSE_OPT_KEY("-c %s", KEY_CONFIG),
|
||||
FUSE_OPT_KEY("restserver=", KEY_SERVER),
|
||||
ZDSFS_OPT("rdw", keepRDW, 1),
|
||||
ZDSFS_OPT("ignore_incomplete", allow_inclomplete_multi_volume, 1),
|
||||
ZDSFS_OPT("check_host_count", host_count, 1),
|
||||
ZDSFS_OPT("restapi", restapi, 1),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
@@ -865,6 +1050,8 @@ static void usage(const char *progname)
|
||||
"ZDSFS options:\n"
|
||||
" -l list_file Text file that contains a list of DASD device"
|
||||
" nodes\n"
|
||||
" -c config_file Text file that contains configuration options\n"
|
||||
" for zdsfs\n"
|
||||
" -o rdw Keep record descriptor words in byte stream\n"
|
||||
" -o ignore_incomplete Continue processing even if parts of a multi"
|
||||
" volume\n"
|
||||
@@ -874,6 +1061,10 @@ static void usage(const char *progname)
|
||||
" size (default 1048576)\n"
|
||||
" -o check_host_count Stop processing if the device is used by another\n"
|
||||
" operating system instance\n"
|
||||
" -o restapi Enable using z/OSMF REST services for coordinated\n"
|
||||
" access to datasets\n"
|
||||
" -o restserver=URL The URL of the z/OSMF REST server to be used for\n"
|
||||
" coordinated access to datasets\n"
|
||||
, progname);
|
||||
}
|
||||
|
||||
@@ -973,6 +1164,67 @@ static void zdsfs_process_device_file(const char *devfile)
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void remove_whitespace(const char *s, char *t)
|
||||
{
|
||||
while (*s != '\0') {
|
||||
if (!isspace(*s)) {
|
||||
*t = *s;
|
||||
t++;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*t = '\0';
|
||||
}
|
||||
|
||||
|
||||
static void zdsfs_process_config_file(const char *config)
|
||||
{
|
||||
char line[MAX_LINE_LENGTH];
|
||||
char *tmp, *key, *value;
|
||||
FILE *fd;
|
||||
char delimiter[] = " =#\n";
|
||||
unsigned long enabled;
|
||||
|
||||
fd = fopen(config, "r");
|
||||
if (!fd) {
|
||||
fprintf(stderr, "could not open file %s: %s\n",
|
||||
config, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fd)) {
|
||||
/* skip empty lines */
|
||||
if (*line == '\n' || *line == '#')
|
||||
continue;
|
||||
|
||||
/* remove all whitespaces */
|
||||
tmp = util_malloc(strlen(line));
|
||||
remove_whitespace(line, tmp);
|
||||
|
||||
key = strtok(tmp, delimiter);
|
||||
if (strcmp(key, "restserver") == 0) {
|
||||
if (zdsfsinfo.nr_server >= MAX_SERVER) {
|
||||
free(tmp);
|
||||
continue;
|
||||
}
|
||||
value = strtok(NULL, delimiter);
|
||||
zdsfsinfo.server[zdsfsinfo.nr_server] =
|
||||
util_strdup(value);
|
||||
zdsfsinfo.nr_server++;
|
||||
} else if (strcmp(key, "restapi") == 0) {
|
||||
value = strtok(NULL, delimiter);
|
||||
enabled = strtoul(value, NULL, 0);
|
||||
if (enabled == 1)
|
||||
zdsfsinfo.restapi = true;
|
||||
} else if (strcmp(key, "keepalive") == 0) {
|
||||
value = strtok(NULL, delimiter);
|
||||
zdsfsinfo.keepalive = strtoul(value, NULL, 0);
|
||||
}
|
||||
free(tmp);
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
@@ -1057,6 +1309,18 @@ static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key,
|
||||
", program version %s\n", RELEASE_STRING);
|
||||
fprintf(stdout, "Copyright IBM Corp. 2013, 2017\n");
|
||||
exit(0);
|
||||
case KEY_CONFIG:
|
||||
/* note that arg starts with "-c" */
|
||||
zdsfsinfo.configfile = util_strdup(arg + 2);
|
||||
return 0;
|
||||
case KEY_SERVER:
|
||||
if (zdsfsinfo.nr_server >= MAX_SERVER)
|
||||
return 0;
|
||||
value = arg + strlen("restserver=");
|
||||
zdsfsinfo.server[zdsfsinfo.nr_server] =
|
||||
util_strdup(value);
|
||||
zdsfsinfo.nr_server++;
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "Unknown argument key %x\n", key);
|
||||
exit(1);
|
||||
@@ -1069,13 +1333,18 @@ int main(int argc, char *argv[])
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
int rc;
|
||||
|
||||
timer_running = 0;
|
||||
bzero(&zdsfsinfo, sizeof(zdsfsinfo));
|
||||
zdsfsinfo.keepRDW = 0;
|
||||
zdsfsinfo.allow_inclomplete_multi_volume = 0;
|
||||
zdsfsinfo.tracks_per_frame = 128;
|
||||
zdsfsinfo.seek_buffer_size = 1048576;
|
||||
zdsfsinfo.configfile = "/etc/zdsfs.conf";
|
||||
zdsfsinfo.keepalive = DEFAULT_KEEPALIVE_SEC;
|
||||
zdsfsinfo.active_server = -1;
|
||||
|
||||
rc = lzds_zdsroot_alloc(&zdsfsinfo.zdsroot);
|
||||
open_dsh = dshlist_alloc();
|
||||
if (rc) {
|
||||
fprintf(stderr, "Could not allocate internal structures\n");
|
||||
exit(1);
|
||||
@@ -1085,6 +1354,7 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr, "Failed to parse option\n");
|
||||
exit(1);
|
||||
}
|
||||
zdsfs_process_config_file(zdsfsinfo.configfile);
|
||||
|
||||
if (!zdsfsinfo.devcount) {
|
||||
fprintf(stderr, "Please specify a block device\n");
|
||||
@@ -1111,9 +1381,21 @@ int main(int argc, char *argv[])
|
||||
if (rc)
|
||||
goto cleanup;
|
||||
|
||||
if (zdsfsinfo.restapi) {
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
zdsfs_test_restserver();
|
||||
if (zdsfsinfo.active_server < 0) {
|
||||
fprintf(stderr, "Error: No z/OSMF REST Server reachable\n");
|
||||
rc = -EACCES;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
rc = fuse_main(args.argc, args.argv, &rdf_oper, NULL);
|
||||
|
||||
cleanup:
|
||||
curl_global_cleanup();
|
||||
dshlist_free(open_dsh);
|
||||
lzds_zdsroot_free(zdsfsinfo.zdsroot);
|
||||
|
||||
fuse_opt_free_args(&args);
|
||||
|
||||
Reference in New Issue
Block a user