zipl/src: added zIPL multienvironment support

Support multiple namespaces in environment block. Use hash
function on "extended" strings for key-value store.
Support multiple sections in environment file.
Modify the import environment precedure to be "section-aware"

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Eduard Shishkin
2022-02-09 20:47:30 +01:00
committed by Jan Höppner
parent 2134aff491
commit 6a439e6771

View File

@@ -23,9 +23,13 @@
#include "envblk.h"
#include "error.h"
#include "misc.h"
#include <assert.h>
/* from linux/fs.h */
#define FIGETBSZ _IO(0x00, 2)
#define KEYWORD_SIZE strlen("[site X]\n")
#define MAX_NR_SITES 10
#define PARAM_SIZE 8
void envblk_create_blank(char *envblk, int envblk_len)
{
@@ -90,21 +94,45 @@ int envblk_size_get(int fd, int *result)
#define LINE_HASH_SIZE (16)
/**
* Calculate hash function of "environment line"
*
* LINE: pointer to a string, containing NANE=VALUE
* LEN: length of the NAME with trailing delimiter "="
/*
* This defines "extended string", an object consisting of
* an optional prefix and a substring
*/
static unsigned int linehash(char *line, int len, unsigned int hash_size)
{
unsigned int sum = 0;
struct estr {
char *prefix; /* pointer to the prefix */
char *str; /* pointer to the substring */
int len; /* length of the substring */
};
/**
* Hash function on "extended strings"
*/
static unsigned int hash_fn(struct estr *estr)
{
char *str = estr->str;
int len = estr->len;
unsigned int sum;
sum = 0;
if (estr->prefix)
sum += 5 * *estr->prefix;
while (len) {
sum += 5 * *(line++);
sum += 5 * *(str++);
len--;
}
return sum % hash_size;
return sum % LINE_HASH_SIZE;
}
/**
* Compare a string STR with an "extended string" ESTR
*/
static int cmp_fn(char *str, struct estr *estr)
{
if (!estr->prefix)
return strncmp(str, estr->str, estr->len);
if (str[0] != *estr->prefix)
return -1;
return strncmp(str + 1, estr->str, estr->len);
}
struct line_hash_entry {
@@ -116,83 +144,86 @@ struct line_hash_entry {
};
/**
* Check if @name meets the requirements for shell environment variables
* (IEEE Std 1003.1-2001), mitigated with lowercase letter allowance
*
* Return 0, if it meets, and 1 otherwise
* Populate a hash TABLE with an item built by an "extended string" ESTR
*/
int envblk_check_name(char *name, int len)
static int hash_table_add(struct line_hash_entry **table, struct estr *estr)
{
int i;
if (len == 0)
return 1;
if (!isalpha(name[0]) && name[0] != '_')
return 1;
for (i = 1; i < len; i++)
if (!isalpha(name[i]) && !isdigit(name[i]) && name[i] != '_')
return 1;
return 0;
}
/**
* Add environment line to hash table
*
* LINE: pointer to a string containing NAME=VALUE
* LEN: length of NAME with trailing delimiter "="
*/
static int hash_table_add(struct line_hash_entry **table, char *line, int len)
{
unsigned int hash;
struct line_hash_entry *new;
unsigned int hash;
int len;
new = malloc(sizeof(*new));
if (!new)
return -ENOMEM;
memset(new, 0, sizeof(*new));
new->line = misc_strdup(line);
if (!new->line) {
free(new);
return -ENOMEM;
len = strlen(estr->str);
if (estr->prefix) {
new->line = misc_malloc(len + 2);
if (!new->line) {
free(new);
return -ENOMEM;
}
new->line[0] = *estr->prefix;
memcpy(new->line + 1, estr->str, len);
new->line[len + 1] = '\0';
} else {
new->line = misc_strdup(estr->str);
if (!new->line) {
free(new);
return -ENOMEM;
}
}
hash = linehash(new->line, len, LINE_HASH_SIZE);
hash = hash_fn(estr);
new->next = table[hash];
table[hash] = new;
return 0;
}
/**
* Find environment line in the hash table by name
*
* LINE: string contailing NAME=VALUE
* LEN: length of the NAME with trailing delimiter "="
* Search an item in the hash table by an "extended string" ESTR
*/
static struct line_hash_entry *hash_table_find(struct line_hash_entry **table,
char *name, size_t len)
struct estr *estr)
{
struct line_hash_entry *item;
item = table[linehash(name, len, LINE_HASH_SIZE)];
item = table[hash_fn(estr)];
while (item) {
if (!strncmp(item->line, name, len))
if (!cmp_fn(item->line, estr))
return item;
item = item->next;
}
return NULL;
}
static int hash_entry_replace(struct line_hash_entry *item, char *name,
/**
* Replace an ITEM with another one built by an "extended string" ESTR
*/
static int hash_entry_replace(struct line_hash_entry *item, struct estr *estr,
int *offset)
{
char *dup;
int len;
char *new;
dup = misc_strdup(name);
if (!dup)
return -ENOMEM;
len = strlen(estr->str);
if (estr->prefix) {
len++;
new = misc_malloc(len + 1);
if (!new)
return -ENOMEM;
new[0] = *estr->prefix;
memcpy(new + 1, estr->str, len - 1);
new[len] = '\0';
} else {
new = misc_strdup(estr->str);
if (!new)
return -ENOMEM;
}
*offset -= strlen(item->line);
free(item->line);
item->line = dup;
*offset += strlen(item->line);
item->line = new;
*offset += len;
return 1;
}
@@ -257,24 +288,106 @@ static void hash_table_flush(struct line_hash_entry **table, char *dst,
}
}
enum keyword_status {
KW_UNKNOWN,
KW_VALID,
KW_MALFORMED,
KW_UNSUPP_ID
};
/**
* Put a zIPL environment line into hash table.
* Return 1, if the line's TAIL is empty, or is a whitespace
* (may be empty) followed by a comment. Otherwise, return 0.
*/
static int ignore_tail(const char *tail)
{
if (*tail == '\n')
return 1;
do {
if (*tail == '#')
return 1;
if (*tail == '\t' || *tail == ' ') {
tail++;
continue;
}
return 0;
} while (*tail != '\n');
return 0;
}
/**
* Check if LINE represents a keyword to identify a section in the
* environment file. Return the result of the check. If the LINE
* represents a valid keyword (KW_VALID is returned), then SITE_ID
* contains ID of the namespace represented by that section.
*
* Identify a name in the line THIS which contains a pair NAME=VALUE, and
* look for an entry in the hash table TABLE with a similar name. If such
* entry exists, then replace the line in that entry with the new one
* specified by THIS. Othewise, insert a new entry to the hash table.
* LINE: a null-terminated string with optional end-of-line symbol
* LEN: number of characters in the LINE excluding NULL-termination
*/
static enum keyword_status check_keyword(char *line, size_t len,
char *site_id, long *val)
{
char *endptr;
if (len < KEYWORD_SIZE || line[len - 1] != '\n')
return KW_UNKNOWN;
if ((line[0] == '[') &&
(line[1] == 's' || line[1] == 'S') &&
(line[2] == 'i' || line[2] == 'I') &&
(line[3] == 't' || line[3] == 'T') &&
(line[4] == 'e' || line[4] == 'E') &&
(line[5] == ' ') && isdigit(line[6])) {
*site_id = line[6];
*val = strtol(&line[6], &endptr, 10);
if (*endptr == ']') {
if (endptr - &line[6] > PARAM_SIZE ||
!ignore_tail(endptr + 1))
return KW_MALFORMED;
return *val < MAX_NR_SITES ? KW_VALID : KW_UNSUPP_ID;
}
return KW_MALFORMED;
}
return KW_UNKNOWN;
}
/**
* Check if @name meets the requirements for shell environment variables
* (IEEE Std 1003.1-2001), mitigated with lowercase letter allowance
*
* THIS: pointer to a NULL-terminated string, which contains the line to
* be imported. If the line was successfully imported, and no line with the
* same NAME was added before, then return 0.
* Return 0, if it meets, and 1 otherwise
*/
int envblk_check_name(char *name, int len)
{
int i;
if (len == 0)
return 1;
if (!isalpha(name[0]) && name[0] != '_')
return 1;
for (i = 1; i < len; i++)
if (!isalpha(name[i]) && !isdigit(name[i]) && name[i] != '_')
return 1;
return 0;
}
/**
* Put a zIPL environment line 'NAME=VALUE' to a namespace referenced by NS_ID
*
* If the line was successfully imported without replacement a line with
* the same NAME, that was added before, then return 0.
* If the line should be ignored, or it replaced previously added line with
* the same NAME, then return > 0. On errors return < 0.
*
* THIS: pointer to a NULL-terminated string, which contains the line to
* be imported.
* NS_ID: Pointer to a numerical character, indicating namespace ID
*/
static int import_one_line(char *this, struct line_hash_entry **table,
int *offset)
int *offset, char *ns_id)
{
struct line_hash_entry *item;
struct estr estr;
char *p;
if (*this == '#')
@@ -289,13 +402,13 @@ static int import_one_line(char *this, struct line_hash_entry **table,
error_reason("Unacceptable name '%.*s'", p - this, this);
return -EINVAL;
}
/*
* Try to find an entry with similar NAME.
*/
item = hash_table_find(table, this, p - this + 1);
estr.prefix = ns_id;
estr.str = this;
estr.len = p - this + 1;
return item != NULL ? hash_entry_replace(item, this, offset) :
hash_table_add(table, this, p - this + 1);
item = hash_table_find(table, &estr);
return item != NULL ? hash_entry_replace(item, &estr, offset) :
hash_table_add(table, &estr);
}
/**
@@ -313,6 +426,8 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
struct line_hash_entry *table[LINE_HASH_SIZE];
unsigned int lines_imported = 0;
char *line = NULL;
char site_id = 0;
long site_id_val;
size_t len = 0;
FILE *stream;
ssize_t read;
@@ -337,6 +452,31 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
if (read < 2)
/* malformed line. Ignored */
continue;
switch (check_keyword(line, read, &site_id, &site_id_val)) {
case KW_VALID:
continue;
case KW_MALFORMED:
/*
* any line not ended with line feed character
* has to be evaluated as KW_UNKNOWN
*/
assert(line[read - 1] == '\n');
line[read - 1] = '\0';
error_reason("%s %s: bad section title '%s'",
err_prefix, from_file, line);
ret = -EINVAL;
goto error;
case KW_UNSUPP_ID:
error_reason("%s %s: unsupported site ID '%li'",
err_prefix, from_file, site_id_val);
ret = -EINVAL;
goto error;
case KW_UNKNOWN:
break;
}
/* LINE is not a secton title */
if (line[read - 1] != '\n' && read == envblk_size - offset) {
/*
* file's tail doesn't contain end-of-line symbol at
@@ -350,7 +490,8 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
ret = -EINVAL;
break;
}
ret = import_one_line(line, table, &offset);
ret = import_one_line(line, table, &offset,
site_id ? &site_id : NULL);
if (ret < 0) {
/* error */
error_text("%s %s", err_prefix, from_file);
@@ -373,6 +514,7 @@ int envblk_import(char *from_file, char *to_buf, int envblk_size)
lines_imported++;
}
hash_table_flush(table, to_buf, sizeof(ZIPL_ENVBLK_SIGNATURE) - 1);
error:
hash_table_free(table);
free(line);
fclose(stream);