[Cluster-devel] [PATCH] libgfs2: Add a gfs2 block query language
Steven Whitehouse
swhiteho at redhat.com
Mon Oct 15 09:48:50 UTC 2012
Hi,
Looks good. Thats a big step forward for us I think,
Steve.
On Fri, 2012-10-12 at 19:51 +0100, Andrew Price wrote:
> This patch adds a small language which we can use to write libgfs2 and
> gfs2-utils tests (among other things). It provides 'get' and 'set'
> statements which look up and modify gfs2 blocks. The language API is
> defined as:
>
> struct lgfs2_lang_state;
>
> struct lgfs2_lang_result {
> uint64_t lr_blocknr;
> struct gfs2_buffer_head *lr_bh;
> const struct lgfs2_metadata *lr_mtype;
> int lr_state; // GFS2_BLKST_*
> };
>
> struct lgfs2_lang_state *lgfs2_lang_init(void);
> void lgfs2_lang_free(struct lgfs2_lang_state **state);
>
> int lgfs2_lang_parsef(struct lgfs2_lang_state *state, FILE *script);
> int lgfs2_lang_parses(struct lgfs2_lang_state *state, const char *script);
>
> struct lgfs2_lang_result *lgfs2_lang_result_next(struct lgfs2_lang_state *state, struct gfs2_sbd *sbd);
> int lgfs2_lang_result_print(struct lgfs2_lang_result *result);
> void lgfs2_lang_result_free(struct lgfs2_lang_result **result);
>
> The lgfs2_lang_parse{s,f} functions allow you to parse a string or a
> file respectively. Using the same state object you can then run the same
> script multiple times, without parsing it again, using
> lgfs2_lang_interpret(). The intended usage of these functions can be
> shown with a simple example (error checking omitted):
>
> struct lgfs2_lang_state *state = lgfs2_lang_init();
> lgfs2_lang_parses(state, "get sb; get 1234 state; set '/foo/bar' {di_entries: 3}");
> for (result = lgfs2_lang_result_next(state, &sbd);
> result != NULL;
> result = lgfs2_lang_result_next(state, &sbd)) {
> lgfs2_lang_result_print(result);
> lgfs2_lang_result_free(&result);
> }
> lgfs2_lang_free(&state);
>
> The language has a simple syntax:
>
> get <block_lookup> [state]
> set <block_lookup> {<field0>:<value0>, <field1>:<value1>, ... }
>
> A block lookup can be a file system block address (1234 or 0x1234), a
> resource group subscript (rgrp[0], rgrp[0x5]), a keyword (sb, master,
> root, rindex) or an offset from any of the above (1234+5, rgrp[1]+23,
> rindex+68, ...).
>
> After the block lookup, the 'get' statement takes an optional keyword
> 'state' which allows you to query the bitmap state of the block.
>
> Examples of 'get':
>
> get rgrp[1]+23 state
> --> result.lr_state == GFS2_BLKST_FREE
>
> get rindex
> --> result.lr_bh is a buffer containing the rindex dinode.
> --> result.lr_mtype is the block's metatype as defined in meta.c.
>
> The 'set' statement requires a list of field-value pairs which are to be
> modified in the block. The field names must match the names shown when
> running the 'get' statement with the same block lookup.
>
> Examples of 'set':
>
> set '/foo/bar/baz' { di_entries: 3 }
> --> result.lr_bh contains the dinode block at path /foo/bar/baz, with
> the di_entries field modified.
> --> result.lr_mtype is the block's metatype as defined in meta.c.
>
> set sb { sb_lockproto: 'lock_dlm', sb_bsize: 0x1000 }
> --> result.lr_bh contains the superblock with the lockproto changed to
> lock_dlm and the block size changed to 4096.
> --> result.lr_mtype is the block's metatype as defined in meta.c.
>
> Whitespace is insignificant in the language and semicolons can be used
> to separate multiple statements. When writing longer scripts spanning
> multiple lines, C-like // syntax can be used to insert comments.
>
> Signed-off-by: Andrew Price <anprice at redhat.com>
> ---
> .gitignore | 7 +
> configure.ac | 2 +
> gfs2/libgfs2/Makefile.am | 13 +-
> gfs2/libgfs2/buf.c | 10 +
> gfs2/libgfs2/fs_ops.c | 6 +-
> gfs2/libgfs2/lang.c | 603 +++++++++++++++++++++++++++++++++++++++++++++++
> gfs2/libgfs2/lang.h | 61 +++++
> gfs2/libgfs2/lexer.l | 101 ++++++++
> gfs2/libgfs2/libgfs2.h | 24 ++
> gfs2/libgfs2/meta.c | 50 ++++
> gfs2/libgfs2/misc.c | 2 -
> gfs2/libgfs2/parser.y | 186 +++++++++++++++
> gfs2/libgfs2/super.c | 2 +-
> 13 files changed, 1057 insertions(+), 10 deletions(-)
> create mode 100644 gfs2/libgfs2/lang.c
> create mode 100644 gfs2/libgfs2/lang.h
> create mode 100644 gfs2/libgfs2/lexer.l
> create mode 100644 gfs2/libgfs2/parser.y
>
> diff --git a/.gitignore b/.gitignore
> index e8b7ea1..4e3071a 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -21,6 +21,9 @@ make/stamp-h1
> m4
> make/clusterautoconfig.h*
> missing
> +ylwrap
> +cscope.out
> +.gdb_history
> *.pc
> .deps
> .libs
> @@ -29,6 +32,10 @@ missing
> *.lo
> gfs2/convert/gfs2_convert
> gfs2/edit/gfs2_edit
> +gfs2/libgfs2/parser.c
> +gfs2/libgfs2/parser.h
> +gfs2/libgfs2/lexer.c
> +gfs2/libgfs2/lexer.h
> gfs2/fsck/fsck.gfs2
> gfs2/mkfs/mkfs.gfs2
> gfs2/mount/mount.gfs2
> diff --git a/configure.ac b/configure.ac
> index d56cfac..ef09569 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -56,6 +56,8 @@ AM_PROG_CC_C_O
> AC_PROG_LN_S
> AC_PROG_INSTALL
> AC_PROG_MAKE_SET
> +AC_PROG_LEX
> +AC_PROG_YACC
>
> ## local helper functions
>
> diff --git a/gfs2/libgfs2/Makefile.am b/gfs2/libgfs2/Makefile.am
> index 4e60b0a..7103e09 100644
> --- a/gfs2/libgfs2/Makefile.am
> +++ b/gfs2/libgfs2/Makefile.am
> @@ -1,15 +1,24 @@
> MAINTAINERCLEANFILES = Makefile.in
>
> -noinst_HEADERS = libgfs2.h
> +CLEANFILES = parser.h parser.c lexer.c lexer.h
> +BUILT_SOURCES = parser.h lexer.h
> +AM_LFLAGS = --header-file=lexer.h
> +AM_YFLAGS = -d
> +
> +noinst_HEADERS = libgfs2.h lang.h
>
> noinst_LTLIBRARIES = libgfs2.la
>
> libgfs2_la_SOURCES = block_list.c fs_bits.c gfs1.c misc.c rgrp.c super.c \
> buf.c fs_geometry.c gfs2_disk_hash.c ondisk.c \
> device_geometry.c fs_ops.c gfs2_log.c recovery.c \
> - structures.c meta.c
> + structures.c meta.c lang.c parser.y lexer.l
>
> libgfs2_la_CPPFLAGS = -D_FILE_OFFSET_BITS=64 \
> -D_LARGEFILE64_SOURCE \
> -D_GNU_SOURCE \
> -I$(top_srcdir)/gfs2/include
> +
> +# Autotools can't handle header files output by flex so we have to generate it manually
> +lexer.h: lexer.l
> + $(LEX) -o lexer.c $(AM_LFLAGS) $^
> diff --git a/gfs2/libgfs2/buf.c b/gfs2/libgfs2/buf.c
> index 956dd8b..5bc1a4e 100644
> --- a/gfs2/libgfs2/buf.c
> +++ b/gfs2/libgfs2/buf.c
> @@ -83,3 +83,13 @@ int brelse(struct gfs2_buffer_head *bh)
> free(bh);
> return error;
> }
> +
> +uint32_t lgfs2_get_block_type(const struct gfs2_buffer_head *lbh)
> +{
> + const struct gfs2_meta_header *mh = lbh->iov.iov_base;
> +
> + if (be32_to_cpu(mh->mh_magic) == GFS2_MAGIC)
> + return be32_to_cpu(mh->mh_type);
> +
> + return 0;
> +}
> diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
> index ec150e8..3d027e8 100644
> --- a/gfs2/libgfs2/fs_ops.c
> +++ b/gfs2/libgfs2/fs_ops.c
> @@ -1759,11 +1759,7 @@ int gfs2_lookupi(struct gfs2_inode *dip, const char *filename, int len,
> return 0;
> }
> error = dir_search(dip, filename, len, NULL, &inum);
> - if (error) {
> - if (error == -ENOENT)
> - return 0;
> - }
> - else
> + if (!error)
> *ipp = lgfs2_inode_read(sdp, inum.no_addr);
>
> return error;
> diff --git a/gfs2/libgfs2/lang.c b/gfs2/libgfs2/lang.c
> new file mode 100644
> index 0000000..12ca7bd
> --- /dev/null
> +++ b/gfs2/libgfs2/lang.c
> @@ -0,0 +1,603 @@
> +#include <stdint.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <sys/queue.h>
> +#include <errno.h>
> +#include <limits.h>
> +#include <ctype.h>
> +
> +#include "parser.h"
> +#include "lang.h"
> +
> +const char* ast_type_string[] = {
> + [AST_NONE] = "NONE",
> + // Statements
> + [AST_ST_SET] = "SET",
> + [AST_ST_GET] = "GET",
> +
> + // Expressions
> + [AST_EX_ID] = "IDENTIFIER",
> + [AST_EX_NUMBER] = "NUMBER",
> + [AST_EX_STRING] = "STRING",
> + [AST_EX_ADDRESS] = "ADDRESS",
> + [AST_EX_PATH] = "PATH",
> + [AST_EX_SUBSCRIPT] = "SUBSCRIPT",
> + [AST_EX_OFFSET] = "OFFSET",
> + [AST_EX_BLOCKSPEC] = "BLOCKSPEC",
> + [AST_EX_STRUCTSPEC] = "STRUCTSPEC",
> + [AST_EX_FIELDSPEC] = "FIELDSPEC",
> +
> + // Keywords
> + [AST_KW_STATE] = "STATE",
> +};
> +
> +/**
> + * Initialize an expression node of the given type from a source string.
> + * Currently just converts numerical values and string values where
> + * appropriate. String values are duplicted into newly allocated buffers as the
> + * text from the parser will go away.
> + * Returns 0 on success or non-zero with errno set on failure
> + */
> +static int ast_expr_init(struct ast_node *expr, ast_node_t type, const char *str)
> +{
> + int ret = 0;
> + switch (type) {
> + case AST_EX_OFFSET:
> + str++; // Cut off the +
> + case AST_EX_NUMBER:
> + ret = sscanf(str, "%"SCNi64, &expr->ast_num);
> + if (ret != 1) {
> + return 1;
> + }
> + break;
> + case AST_EX_ID:
> + case AST_EX_PATH:
> + case AST_EX_STRING:
> + expr->ast_str = strdup(str);
> + if (expr->ast_str == NULL) {
> + return 1;
> + }
> + break;
> + case AST_EX_ADDRESS:
> + case AST_EX_SUBSCRIPT:
> + case AST_EX_BLOCKSPEC:
> + case AST_EX_STRUCTSPEC:
> + case AST_EX_FIELDSPEC:
> + case AST_KW_STATE:
> + break;
> + default:
> + errno = EINVAL;
> + return 1;
> + }
> + return 0;
> +}
> +
> +/**
> + * Create a new AST node of a given type from a source string.
> + * Returns a pointer to the new node or NULL on failure with errno set.
> + */
> +struct ast_node *ast_new(ast_node_t type, const char *text)
> +{
> + struct ast_node *node;
> + node = (struct ast_node *)calloc(1, sizeof(struct ast_node));
> + if (node == NULL) {
> + goto return_fail;
> + }
> +
> + if (type > _AST_EX_START && ast_expr_init(node, type, text)) {
> + goto return_free;
> + }
> +
> + node->ast_text = strdup(text);
> + if (node->ast_text == NULL) {
> + goto return_free;
> + }
> + node->ast_type = type;
> +
> + return node;
> +
> +return_free:
> + if (node->ast_text) {
> + free(node->ast_text);
> + }
> + if (node->ast_str) {
> + free(node->ast_str);
> + }
> + free(node);
> +return_fail:
> + fprintf(stderr, "Failed to create new value from %s: %s\n", text, strerror(errno));
> + return NULL;
> +}
> +
> +/**
> + * Free the memory allocated for an AST node and set its pointer to NULL
> + */
> +void ast_destroy(struct ast_node **node)
> +{
> + if (*node == NULL) {
> + return;
> + }
> + ast_destroy(&(*node)->ast_left);
> + ast_destroy(&(*node)->ast_right);
> + switch((*node)->ast_type) {
> + case AST_EX_ID:
> + case AST_EX_PATH:
> + case AST_EX_STRING:
> + free((*node)->ast_str);
> + break;
> + default:
> + break;
> + }
> + free((*node)->ast_text);
> + free(*node);
> + *node = NULL;
> +}
> +
> +static void ast_string_unescape(char *str)
> +{
> + int head, tail;
> + for (head = tail = 0; str[head] != '\0'; head++, tail++) {
> + if (str[head] == '\\' && str[head+1] != '\0')
> + head++;
> + str[tail] = str[head];
> + }
> + str[tail] = '\0';
> +}
> +
> +static uint64_t ast_lookup_path(char *path, struct gfs2_sbd *sbd)
> +{
> + int err = 0;
> + char *c;
> + struct gfs2_inode *ip, *iptmp;
> + char *segment;
> + uint64_t bn = 0;
> +
> + segment = strtok_r(path, "/", &c);
> + ip = lgfs2_inode_read(sbd, sbd->sd_sb.sb_root_dir.no_addr);
> +
> + while (ip != NULL) {
> + if (segment == NULL) { // No more segments
> + bn = ip->i_di.di_num.no_addr;
> + inode_put(&ip);
> + return bn;
> + }
> + ast_string_unescape(segment);
> + err = gfs2_lookupi(ip, segment, strlen(segment), &iptmp);
> + inode_put(&ip);
> + if (err != 0) {
> + errno = -err;
> + break;
> + }
> + ip = iptmp;
> + segment = strtok_r(NULL, "/", &c);
> + }
> +
> + perror("Path lookup");
> + return 0;
> +}
> +
> +enum block_id {
> + ID_SB = 0,
> + ID_MASTER,
> + ID_ROOT,
> + ID_RINDEX,
> +
> + ID_END
> +};
> +
> +/**
> + * Names of blocks which can be uniquely identified in the fs
> + */
> +static const char *block_ids[] = {
> + [ID_SB] = "sb",
> + [ID_MASTER] = "master",
> + [ID_ROOT] = "root",
> + [ID_RINDEX] = "rindex",
> +
> + [ID_END] = NULL
> +};
> +
> +static uint64_t ast_lookup_id(const char *id, struct gfs2_sbd *sbd)
> +{
> + uint64_t bn = 0;
> + int i;
> + for (i = 0; i < ID_END; i++) {
> + if (!strcmp(id, block_ids[i])) {
> + break;
> + }
> + }
> + switch (i) {
> + case ID_SB:
> + bn = sbd->sb_addr;
> + break;
> + case ID_MASTER:
> + bn = sbd->sd_sb.sb_master_dir.no_addr;
> + break;
> + case ID_ROOT:
> + bn = sbd->sd_sb.sb_root_dir.no_addr;
> + break;
> + case ID_RINDEX:
> + bn = sbd->md.riinode->i_di.di_num.no_addr;
> + break;
> + default:
> + return 0;
> + }
> + return bn;
> +}
> +
> +static uint64_t ast_lookup_rgrp(uint64_t rgnum, struct gfs2_sbd *sbd)
> +{
> + uint64_t i = rgnum;
> + struct osi_node *n;
> +
> + for (n = osi_first(&sbd->rgtree); n != NULL && i > 0; n = osi_next(n), i--);
> + if (n != NULL && i == 0)
> + return ((struct rgrp_tree *)n)->ri.ri_addr;
> + fprintf(stderr, "Resource group number out of range: %"PRIu64"\n", rgnum);
> + return 0;
> +}
> +
> +static uint64_t ast_lookup_subscript(struct ast_node *id, struct ast_node *index,
> + struct gfs2_sbd *sbd)
> +{
> + uint64_t bn = 0;
> + const char *name = id->ast_str;
> + if (!strcmp(name, "rgrp")) {
> + bn = ast_lookup_rgrp(index->ast_num, sbd);
> + } else {
> + fprintf(stderr, "Unrecognized identifier %s\n", name);
> + }
> + return bn;
> +}
> +
> +/**
> + * Look up a block and return its number. The kind of lookup depends on the
> + * type of the ast node.
> + */
> +static uint64_t ast_lookup_block_num(struct ast_node *ast, struct gfs2_sbd *sbd)
> +{
> + uint64_t bn = 0;
> + switch (ast->ast_type) {
> + case AST_EX_OFFSET:
> + bn = ast_lookup_block_num(ast->ast_left, sbd) + ast->ast_num;
> + break;
> + case AST_EX_ADDRESS:
> + bn = ast->ast_num;
> + break;
> + case AST_EX_PATH:
> + bn = ast_lookup_path(ast->ast_str, sbd);
> + break;
> + case AST_EX_ID:
> + bn = ast_lookup_id(ast->ast_str, sbd);
> + break;
> + case AST_EX_SUBSCRIPT:
> + bn = ast_lookup_subscript(ast->ast_left, ast->ast_left->ast_left, sbd);
> + break;
> + default:
> + break;
> + }
> + return bn;
> +}
> +
> +static struct gfs2_buffer_head *ast_lookup_block(struct ast_node *node, struct gfs2_sbd *sbd)
> +{
> + uint64_t bn = ast_lookup_block_num(node, sbd);
> + if (bn == 0) {
> + return NULL;
> + }
> +
> + return bread(sbd, bn);
> +}
> +
> +static const char *bitstate_strings[] = {
> + [GFS2_BLKST_FREE] = "Free",
> + [GFS2_BLKST_USED] = "Used",
> + [GFS2_BLKST_UNLINKED] = "Unlinked",
> + [GFS2_BLKST_DINODE] = "Dinode"
> +};
> +
> +/**
> + * Print a representation of an arbitrary GFS2 block to stdout
> + */
> +int lgfs2_lang_result_print(struct lgfs2_lang_result *result)
> +{
> + int i;
> + if (result->lr_mtype != NULL) {
> + for (i = 0; i < result->lr_mtype->nfields; i++) {
> + lgfs2_field_print(result->lr_bh, result->lr_mtype, &result->lr_mtype->fields[i]);
> + }
> + } else {
> + printf("%"PRIu64": %s\n", result->lr_blocknr, bitstate_strings[result->lr_state]);
> + }
> + return 0;
> +}
> +
> +static int ast_get_bitstate(uint64_t bn, struct gfs2_sbd *sbd)
> +{
> + int ret = 0;
> + int state = 0;
> + struct rgrp_tree *rgd = gfs2_blk2rgrpd(sbd, bn);
> + if (rgd == NULL) {
> + fprintf(stderr, "Could not find resource group for block %"PRIu64"\n", bn);
> + return -1;
> + }
> +
> + ret = gfs2_rgrp_read(sbd, rgd);
> + if (ret != 0) {
> + fprintf(stderr, "Failed to read resource group for block %"PRIu64": %d\n", bn, ret);
> + return -1;
> + }
> +
> + state = gfs2_get_bitmap(sbd, bn, rgd);
> + if (state == -1) {
> + fprintf(stderr, "Failed to acquire bitmap state for block %"PRIu64"\n", bn);
> + return -1;
> + }
> +
> + gfs2_rgrp_relse(rgd);
> + return state;
> +}
> +
> +static const struct lgfs2_metadata *ast_lookup_mtype(const struct gfs2_buffer_head *bh)
> +{
> + const struct lgfs2_metadata *mtype;
> + const uint32_t mh_type = lgfs2_get_block_type(bh);
> + if (mh_type == 0) {
> + fprintf(stderr, "Could not determine type for block %"PRIu64"\n", bh->b_blocknr);
> + return NULL;
> + }
> +
> + mtype = lgfs2_find_mtype(mh_type, bh->sdp->gfs1 ? LGFS2_MD_GFS1 : LGFS2_MD_GFS2);
> + if (mtype == NULL) {
> + fprintf(stderr, "Could not determine meta type for block %"PRIu64"\n", bh->b_blocknr);
> + return NULL;
> + }
> + return mtype;
> +}
> +
> +/**
> + * Interpret the get statement.
> + */
> +static struct lgfs2_lang_result *ast_interp_get(struct lgfs2_lang_state *state,
> + struct ast_node *ast, struct gfs2_sbd *sbd)
> +{
> + struct lgfs2_lang_result *result = calloc(1, sizeof(struct lgfs2_lang_result));
> + if (result == NULL) {
> + fprintf(stderr, "Failed to allocate memory for result\n");
> + return NULL;
> + }
> +
> + if (ast->ast_right->ast_right == NULL) {
> + result->lr_bh = ast_lookup_block(ast->ast_right, sbd);
> + if (result->lr_bh == NULL) {
> + free(result);
> + return NULL;
> + }
> + result->lr_blocknr = result->lr_bh->b_blocknr;
> + result->lr_mtype = ast_lookup_mtype(result->lr_bh);
> +
> + } else if (ast->ast_right->ast_right->ast_type == AST_KW_STATE) {
> + result->lr_blocknr = ast_lookup_block_num(ast->ast_right, sbd);
> + if (result->lr_blocknr == 0) {
> + return NULL;
> + }
> + result->lr_state = ast_get_bitstate(result->lr_blocknr, sbd);
> + }
> +
> + return result;
> +}
> +
> +/**
> + * Interpret a UUID string by removing hyphens from the string and then
> + * interprets 16 pairs of hex digits as octets.
> + */
> +static int ast_str_to_uuid(const char *str, uint8_t *uuid)
> +{
> + char s[33];
> + int head, tail, tmp;
> +
> + for (head = tail = 0; head < strlen(str) && tail < 33; head++) {
> + if (str[head] == '-')
> + continue;
> + s[tail] = tolower(str[head]);
> + if (!((s[tail] >= 'a' && s[tail] <= 'f') ||
> + (s[tail] >= '0' && s[tail] <= '9')))
> + goto invalid;
> + tail++;
> + }
> + if (tail != 32) {
> + goto invalid;
> + }
> + s[tail] = '\0';
> + for (head = 0; head < 16; head++) {
> + if (sscanf(s+(head*2), "%02x", &tmp) != 1) {
> + goto invalid;
> + }
> + *(uuid + head) = tmp;
> + }
> + return AST_INTERP_SUCCESS;
> +invalid:
> + fprintf(stderr, "Invalid UUID\n");
> + return AST_INTERP_INVAL;
> +}
> +
> +/**
> + * Set a field of a gfs2 block of a given type to a given value.
> + * Returns AST_INTERP_* to signal success, an invalid field/value or an error.
> + */
> +static int ast_field_set(struct gfs2_buffer_head *bh, const struct lgfs2_metafield *field,
> + struct ast_node *val)
> +{
> + char *fieldp = (char *)bh->iov.iov_base + field->offset;
> +
> + if (field->flags & LGFS2_MFF_UUID) {
> + uint8_t uuid[16];
> + int ret = ast_str_to_uuid(val->ast_str, uuid);
> +
> + if (ret != AST_INTERP_SUCCESS)
> + return ret;
> +
> + memcpy(fieldp, uuid, 16);
> + bmodified(bh);
> + return AST_INTERP_SUCCESS;
> + }
> +
> + if ((field->flags & LGFS2_MFF_STRING) && strlen(val->ast_str) > field->length) {
> + fprintf(stderr, "String '%s' is too long for field '%s'\n", val->ast_str, field->name);
> + return AST_INTERP_INVAL;
> + }
> +
> + if (field->flags & (LGFS2_MFF_STRING|LGFS2_MFF_UUID)) {
> + strncpy(fieldp, val->ast_str, field->length - 1);
> + fieldp[field->length - 1] = '\0';
> + bmodified(bh);
> + return AST_INTERP_SUCCESS;
> + } else {
> + // Numeric fields
> + switch(field->length) {
> + case 1:
> + if (val->ast_num > UINT8_MAX)
> + break;
> + *fieldp = (uint8_t)val->ast_num;
> + bmodified(bh);
> + return AST_INTERP_SUCCESS;
> + case 2:
> + if (val->ast_num > UINT16_MAX)
> + break;
> + *(uint16_t *)fieldp = cpu_to_be16((uint16_t)val->ast_num);
> + bmodified(bh);
> + return AST_INTERP_SUCCESS;
> + case 4:
> + if (val->ast_num > UINT32_MAX)
> + break;
> + *(uint32_t *)fieldp = cpu_to_be32((uint32_t)val->ast_num);
> + bmodified(bh);
> + return AST_INTERP_SUCCESS;
> + case 8:
> + *(uint64_t *)fieldp = cpu_to_be64((uint64_t)val->ast_num);
> + bmodified(bh);
> + return AST_INTERP_SUCCESS;
> + default:
> + // This should never happen
> + return AST_INTERP_ERR;
> + }
> + }
> +
> + fprintf(stderr, "Invalid field assignment: %s (size %d) = %s\n",
> + field->name, field->length, val->ast_text);
> + return AST_INTERP_INVAL;
> +}
> +
> +/**
> + * Interpret an assignment (set)
> + */
> +static struct lgfs2_lang_result *ast_interp_set(struct lgfs2_lang_state *state,
> + struct ast_node *ast, struct gfs2_sbd *sbd)
> +{
> + struct ast_node *lookup = ast->ast_right;
> + struct ast_node *fieldspec;
> + struct ast_node *fieldname;
> + struct ast_node *fieldval;
> + uint32_t mh_type = 0;
> + int i = 0;
> + int ret = 0;
> +
> + struct lgfs2_lang_result *result = calloc(1, sizeof(struct lgfs2_lang_result));
> + if (result == NULL) {
> + fprintf(stderr, "Failed to allocate memory for result\n");
> + return NULL;
> + }
> +
> + result->lr_bh = ast_lookup_block(lookup, sbd);
> + if (result->lr_bh == NULL) {
> + goto out_err;
> + }
> +
> + mh_type = lgfs2_get_block_type(result->lr_bh);
> + if (mh_type == 0) {
> + goto out_err;
> + }
> +
> + result->lr_mtype = lgfs2_find_mtype(mh_type, sbd->gfs1 ? LGFS2_MD_GFS1 : LGFS2_MD_GFS2);
> + if (result->lr_mtype == NULL) {
> + goto out_err;
> + }
> +
> + for (fieldspec = lookup->ast_right;
> + fieldspec != NULL && fieldspec->ast_type == AST_EX_FIELDSPEC;
> + fieldspec = fieldspec->ast_left) {
> +
> + fieldname = fieldspec->ast_right;
> + fieldval = fieldname->ast_right;
> + for (i = 0; i < result->lr_mtype->nfields; i++) {
> + if (!strcmp(result->lr_mtype->fields[i].name, fieldname->ast_str)) {
> + ret = ast_field_set(result->lr_bh, &result->lr_mtype->fields[i], fieldval);
> + if (ret != AST_INTERP_SUCCESS) {
> + goto out_err;
> + }
> + break;
> + }
> + }
> + }
> +
> + ret = bwrite(result->lr_bh);
> + if (ret != 0) {
> + fprintf(stderr, "Failed to write modified block %"PRIu64": %s\n",
> + result->lr_bh->b_blocknr, strerror(errno));
> + goto out_err;
> + }
> +
> + return result;
> +
> +out_err:
> + lgfs2_lang_result_free(&result);
> + return NULL;
> +}
> +
> +static struct lgfs2_lang_result *ast_interpret_node(struct lgfs2_lang_state *state,
> + struct ast_node *ast, struct gfs2_sbd *sbd)
> +{
> + struct lgfs2_lang_result *result = NULL;
> +
> + if (ast->ast_type == AST_ST_SET) {
> + result = ast_interp_set(state, ast, sbd);
> + } else if (ast->ast_type == AST_ST_GET) {
> + result = ast_interp_get(state, ast, sbd);
> + } else {
> + fprintf(stderr, "Invalid AST node type: %d\n", ast->ast_type);
> + }
> + return result;
> +}
> +
> +struct lgfs2_lang_result *lgfs2_lang_result_next(struct lgfs2_lang_state *state,
> + struct gfs2_sbd *sbd)
> +{
> + struct lgfs2_lang_result *result;
> + if (state->ls_interp_curr == NULL) {
> + return NULL;
> + }
> + result = ast_interpret_node(state, state->ls_interp_curr, sbd);
> + if (result == NULL) {
> + return NULL;
> + }
> + state->ls_interp_curr = state->ls_interp_curr->ast_left;
> + return result;
> +}
> +
> +void lgfs2_lang_result_free(struct lgfs2_lang_result **result)
> +{
> + if (*result == NULL) {
> + fprintf(stderr, "Warning: attempted to free a null result\n");
> + return;
> + }
> +
> + if ((*result)->lr_mtype != NULL) {
> + (*result)->lr_bh->b_modified = 0;
> + brelse((*result)->lr_bh);
> + (*result)->lr_bh = NULL;
> + }
> +
> + free(*result);
> + *result = NULL;
> +}
> diff --git a/gfs2/libgfs2/lang.h b/gfs2/libgfs2/lang.h
> new file mode 100644
> index 0000000..955e52e
> --- /dev/null
> +++ b/gfs2/libgfs2/lang.h
> @@ -0,0 +1,61 @@
> +#ifndef LANG_H
> +#define LANG_H
> +#include <stdint.h>
> +#include "libgfs2.h"
> +
> +struct lgfs2_lang_state {
> + int ls_colnum;
> + int ls_linenum;
> + int ls_errnum;
> + struct ast_node *ls_ast_root;
> + struct ast_node *ls_ast_tail;
> + struct ast_node *ls_interp_curr;
> +};
> +
> +typedef enum {
> + AST_NONE,
> + // Statements
> + AST_ST_SET,
> + AST_ST_GET,
> +
> + _AST_EX_START,
> + // Expressions
> + AST_EX_ID,
> + AST_EX_NUMBER,
> + AST_EX_STRING,
> + AST_EX_ADDRESS,
> + AST_EX_PATH,
> + AST_EX_SUBSCRIPT,
> + AST_EX_OFFSET,
> + AST_EX_BLOCKSPEC,
> + AST_EX_STRUCTSPEC,
> + AST_EX_FIELDSPEC,
> +
> + // Keywords
> + AST_KW_STATE,
> +} ast_node_t;
> +
> +enum {
> + AST_INTERP_SUCCESS = 0, // Success
> + AST_INTERP_FAIL = 1, // Failure
> + AST_INTERP_INVAL = 2, // Invalid field/type mismatch
> + AST_INTERP_ERR = 3, // Something went wrong, see errno
> +};
> +
> +extern const char* ast_type_string[];
> +
> +struct ast_node {
> + ast_node_t ast_type;
> + struct ast_node *ast_left;
> + struct ast_node *ast_right;
> + char *ast_text;
> + char *ast_str;
> + uint64_t ast_num;
> +};
> +
> +extern struct ast_node *ast_new(ast_node_t type, const char *text);
> +extern void ast_destroy(struct ast_node **val);
> +
> +#define YYSTYPE struct ast_node *
> +
> +#endif /* LANG_H */
> diff --git a/gfs2/libgfs2/lexer.l b/gfs2/libgfs2/lexer.l
> new file mode 100644
> index 0000000..36e1c2d
> --- /dev/null
> +++ b/gfs2/libgfs2/lexer.l
> @@ -0,0 +1,101 @@
> +%{
> +#include "lang.h"
> +#include "parser.h"
> +
> +#define EXTRA ((struct lgfs2_lang_state *)yyextra)
> +
> +#define P(token, type, text) do {\
> + *(yylval) = ast_new(type, text);\
> + if (*(yylval) == NULL) {\
> + EXTRA->ls_errnum = errno;\
> + return 1;\
> + }\
> + return (TOK_##token);\
> +} while(0)
> +
> +#define COLNUM EXTRA->ls_colnum
> +#define YY_USER_ACTION COLNUM += yyleng;
> +
> +%}
> +%option bison-bridge reentrant
> +%option warn debug
> +%option nounput noinput
> +%option noyywrap
> +%option extra-type="struct lgfs2_lang_state *"
> +
> +letter [a-zA-Z_]
> +decdigit [0-9]
> +decnumber -?{decdigit}+
> +hexdigit [0-9a-fA-F]
> +hexnumber -?0x{hexdigit}+
> +number ({decnumber}|{hexnumber})
> +offset \+{number}
> +id {letter}({letter}|{decdigit}|\.)*
> +string \'([^\']|\\\')*\'
> +comment \/\/.*\n
> +whitespace [ \t\r]+
> +
> +%%
> +
> +\{ {
> + return TOK_LBRACE;
> + }
> +\} {
> + return TOK_RBRACE;
> + }
> +\[ {
> + return TOK_LBRACKET;
> + }
> +\] {
> + P(RBRACKET, AST_EX_SUBSCRIPT, "[ ]");
> + }
> +\, {
> + return TOK_COMMA;
> + }
> +\: {
> + P(COLON, AST_EX_FIELDSPEC, yytext);
> + }
> +\; {
> + return TOK_SEMI;
> + }
> +set {
> + P(SET, AST_ST_SET, yytext);
> + }
> +get {
> + P(GET, AST_ST_GET, yytext);
> + }
> +state {
> + P(STATE, AST_KW_STATE, yytext);
> + }
> +{string} {
> + yytext[yyleng-1] = '\0';
> + P(STRING, AST_EX_STRING, yytext + 1);
> + }
> +{offset} {
> + P(OFFSET, AST_EX_OFFSET, yytext);
> + }
> +{number} {
> + P(NUMBER, AST_EX_NUMBER, yytext);
> + }
> +{id} {
> + P(ID, AST_EX_ID, yytext);
> + }
> +{comment} {
> + COLNUM = 0;
> + EXTRA->ls_linenum++;
> + }
> +<<EOF>> {
> + return 0;
> + }
> +\n {
> + COLNUM = 0;
> + EXTRA->ls_linenum++;
> + }
> +{whitespace} ;
> +. {
> + printf("Unexpected character '%s' on line %d column %d\n",
> + yytext, yylineno, COLNUM);
> + return 1;
> + }
> +
> +%%
> diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
> index 74ee2d0..3045337 100644
> --- a/gfs2/libgfs2/libgfs2.h
> +++ b/gfs2/libgfs2/libgfs2.h
> @@ -356,6 +356,10 @@ extern const unsigned lgfs2_ld_type_size;
> extern const struct lgfs2_symbolic lgfs2_ld1_types[];
> extern const unsigned lgfs2_ld1_type_size;
> extern int lgfs2_selfcheck(void);
> +extern const struct lgfs2_metadata *lgfs2_find_mtype(uint32_t mh_type, const unsigned versions);
> +extern int lgfs2_field_print(const struct gfs2_buffer_head *bh,
> + const struct lgfs2_metadata *mtype,
> + const struct lgfs2_metafield *field);
>
> /* bitmap.c */
> struct gfs2_bmap {
> @@ -379,6 +383,7 @@ extern struct gfs2_buffer_head *__bread(struct gfs2_sbd *sdp, uint64_t num,
> int line, const char *caller);
> extern int bwrite(struct gfs2_buffer_head *bh);
> extern int brelse(struct gfs2_buffer_head *bh);
> +extern uint32_t lgfs2_get_block_type(const struct gfs2_buffer_head *lbh);
>
> #define bmodified(bh) do { bh->b_modified = 1; } while(0)
>
> @@ -828,6 +833,25 @@ extern void gfs2_log_descriptor_print(struct gfs2_log_descriptor *ld);
> extern void gfs2_statfs_change_print(struct gfs2_statfs_change *sc);
> extern void gfs2_quota_change_print(struct gfs2_quota_change *qc);
>
> +/* Language functions */
> +
> +struct lgfs2_lang_state;
> +
> +struct lgfs2_lang_result {
> + uint64_t lr_blocknr;
> + struct gfs2_buffer_head *lr_bh;
> + const struct lgfs2_metadata *lr_mtype;
> + int lr_state; // GFS2_BLKST_*
> +};
> +
> +extern struct lgfs2_lang_state *lgfs2_lang_init(void);
> +extern int lgfs2_lang_parsef(struct lgfs2_lang_state *state, FILE *script);
> +extern int lgfs2_lang_parses(struct lgfs2_lang_state *state, const char *script);
> +extern struct lgfs2_lang_result *lgfs2_lang_result_next(struct lgfs2_lang_state *state, struct gfs2_sbd *sbd);
> +extern int lgfs2_lang_result_print(struct lgfs2_lang_result *result);
> +extern void lgfs2_lang_result_free(struct lgfs2_lang_result **result);
> +extern void lgfs2_lang_free(struct lgfs2_lang_state **state);
> +
> __END_DECLS
>
> #endif /* __LIBGFS2_DOT_H__ */
> diff --git a/gfs2/libgfs2/meta.c b/gfs2/libgfs2/meta.c
> index a677cdc..29bf239 100644
> --- a/gfs2/libgfs2/meta.c
> +++ b/gfs2/libgfs2/meta.c
> @@ -808,3 +808,53 @@ int lgfs2_selfcheck(void)
> return ret;
> }
>
> +const struct lgfs2_metadata *lgfs2_find_mtype(uint32_t mh_type, const unsigned versions)
> +{
> + const struct lgfs2_metadata *m = lgfs2_metadata;
> + unsigned n = 0;
> +
> + do {
> + if ((m[n].versions & versions) && m[n].mh_type == mh_type)
> + return &m[n];
> + n++;
> + } while (n < lgfs2_metadata_size);
> +
> + return NULL;
> +}
> +
> +/**
> + * Print a representation of an arbitrary field of an arbitrary GFS2 block to stdout
> + * Returns 0 if successful, 1 otherwise
> + */
> +int lgfs2_field_print(const struct gfs2_buffer_head *bh, const struct lgfs2_metadata *mtype,
> + const struct lgfs2_metafield *field)
> +{
> + const char *fieldp = (char *)bh->iov.iov_base + field->offset;
> +
> + printf("%s\t%"PRIu64"\t%u\t%u\t%s\t", mtype->name, bh->b_blocknr, field->offset, field->length, field->name);
> + if (field->flags & LGFS2_MFF_UUID) {
> + printf("'%s'\n", str_uuid((const unsigned char *)fieldp));
> + } else if (field->flags & LGFS2_MFF_STRING) {
> + printf("'%s'\n", fieldp);
> + } else {
> + switch(field->length) {
> + case 1:
> + printf("%"PRIu8"\n", *(uint8_t *)fieldp);
> + break;
> + case 2:
> + printf("%"PRIu16"\n", be16_to_cpu(*(uint16_t *)fieldp));
> + break;
> + case 4:
> + printf("%"PRIu32"\n", be32_to_cpu(*(uint32_t *)fieldp));
> + break;
> + case 8:
> + printf("%"PRIu64"\n", be64_to_cpu(*(uint64_t *)fieldp));
> + break;
> + default:
> + // "Reserved" field so just print 0
> + printf("0\n");
> + return 1;
> + }
> + }
> + return 0;
> +}
> diff --git a/gfs2/libgfs2/misc.c b/gfs2/libgfs2/misc.c
> index a68da4a..c2eb245 100644
> --- a/gfs2/libgfs2/misc.c
> +++ b/gfs2/libgfs2/misc.c
> @@ -26,8 +26,6 @@
> #define SYS_BASE "/sys/fs/gfs2" /* FIXME: Look in /proc/mounts to find this */
> #define DIV_RU(x, y) (((x) + (y) - 1) / (y))
>
> -static char sysfs_buf[PAGE_SIZE];
> -
> int compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize,
> uint32_t *maxheight, uint32_t bsize1, int diptrs, int inptrs)
> {
> diff --git a/gfs2/libgfs2/parser.y b/gfs2/libgfs2/parser.y
> new file mode 100644
> index 0000000..084d15e
> --- /dev/null
> +++ b/gfs2/libgfs2/parser.y
> @@ -0,0 +1,186 @@
> +%code top {
> +#include <errno.h>
> +#include "lang.h"
> +#include "lexer.h"
> +
> +static int yyerror(struct lgfs2_lang_state *state, yyscan_t lexer, const char *errorstr)
> +{
> + fprintf(stderr, "%d:%d: %s\n", state->ls_linenum, state->ls_colnum, errorstr);
> + return 1;
> +}
> +
> +}
> +%defines
> +%debug
> +%define api.pure
> +%parse-param { struct lgfs2_lang_state *state }
> +%parse-param { yyscan_t lexer }
> +%lex-param { yyscan_t lexer }
> +%start script
> +%token TOK_COLON
> +%token TOK_COMMA
> +%token TOK_ID
> +%token TOK_LBRACE
> +%token TOK_LBRACKET
> +%token TOK_NUMBER
> +%token TOK_OFFSET
> +%token TOK_RBRACE
> +%token TOK_RBRACKET
> +%token TOK_SEMI
> +%token TOK_SET
> +%token TOK_GET
> +%token TOK_STATE
> +%token TOK_STRING
> +%%
> +script: statements {
> + state->ls_ast_root = $1;
> + state->ls_interp_curr = $1;
> + }
> + | statements TOK_SEMI {
> + state->ls_ast_root = $1;
> + state->ls_interp_curr = $1;
> + }
> +;
> +statements: statements TOK_SEMI statement {
> + state->ls_ast_tail->ast_left = $3;
> + state->ls_ast_tail = $3;
> + $$ = $1;
> + }
> + | statement {
> + if (state->ls_ast_tail == NULL)
> + state->ls_ast_tail = $1;
> + $$ = $1;
> + }
> +;
> +
> +statement: set_stmt { $$ = $1; }
> + | get_stmt { $$ = $1; }
> +;
> +set_stmt: TOK_SET blockspec structspec {
> + $1->ast_right = $2;
> + $2->ast_right = $3;
> + $$ = $1;
> + }
> +;
> +get_stmt: TOK_GET blockspec { $1->ast_right = $2; $$ = $1; }
> + | TOK_GET blockspec TOK_STATE {
> + $1->ast_right = $2;
> + $2->ast_right = $3;
> + $$ = $1;
> + }
> +;
> +blockspec: offset { $$ = $1; }
> + | address { $$ = $1; }
> + | path { $$ = $1; }
> + | block_literal { $$ = $1; }
> + | subscript { $$ = $1; }
> +;
> +offset: blockspec TOK_OFFSET {
> + $2->ast_left = $1;
> + $$ = $2;
> + }
> +;
> +block_literal: identifier { $$ = $1; }
> +;
> +subscript: block_literal TOK_LBRACKET index TOK_RBRACKET {
> + $4->ast_left = $1;
> + $1->ast_left = $3;
> + $$ = $4;
> + }
> +;
> +index: number { $$ = $1; }
> + | identifier { $$ = $1; }
> +;
> +address: number { $1->ast_type = AST_EX_ADDRESS; $$ = $1; }
> +;
> +path: string {
> + if (*($1->ast_str) != '/') {
> + fprintf(stderr, "Path doesn't begin with '/': %s\n", $1->ast_str);
> + YYABORT;
> + }
> + $1->ast_type = AST_EX_PATH;
> + $$ = $1;
> + }
> +;
> +structspec: TOK_LBRACE fieldspecs TOK_RBRACE { $$ = $2; }
> +;
> +fieldspecs: fieldspecs TOK_COMMA fieldspec { $1->ast_left = $3; $$ = $1; }
> + | fieldspec { $$ = $1; }
> +;
> +fieldspec: identifier TOK_COLON fieldvalue {
> + $2->ast_right = $1;
> + $1->ast_right = $3;
> + $$ = $2;
> + }
> +;
> +fieldvalue: number { $$ = $1; }
> + | string { $$ = $1; }
> +;
> +number: TOK_NUMBER { $$ = $1; }
> +string: TOK_STRING { $$ = $1; }
> +identifier: TOK_ID { $$ = $1; }
> +%%
> +
> +/**
> + * Allocate and initialize a new parse state structure. The caller must free the
> + * memory returned by this function.
> + */
> +struct lgfs2_lang_state *lgfs2_lang_init(void)
> +{
> + struct lgfs2_lang_state *state;
> + state = calloc(1, sizeof(struct lgfs2_lang_state));
> + if (state == NULL) {
> + return NULL;
> + }
> + state->ls_linenum = 1;
> + return state;
> +}
> +
> +void lgfs2_lang_free(struct lgfs2_lang_state **state)
> +{
> + ast_destroy(&(*state)->ls_ast_root);
> + free(*state);
> + *state = NULL;
> +}
> +
> +int lgfs2_lang_parsef(struct lgfs2_lang_state *state, FILE *src)
> +{
> + int ret = 0;
> + yyscan_t lexer;
> +
> + ret = yylex_init_extra(state, &lexer);
> + if (ret != 0) {
> + fprintf(stderr, "Failed to initialize lexer.\n");
> + return ret;
> + }
> +
> + yyset_in(src, lexer);
> + ret = yyparse(state, lexer);
> + yylex_destroy(lexer);
> + return ret;
> +}
> +
> +int lgfs2_lang_parses(struct lgfs2_lang_state *state, const char *cstr)
> +{
> + int ret;
> + FILE *src;
> + char *str = strdup(cstr);
> +
> + if (str == NULL) {
> + perror("Failed to duplicate source string");
> + return 1;
> + }
> + src = fmemopen(str, strlen(str), "r");
> + if (src == NULL) {
> + perror("Failed to open string as source file");
> + free(str);
> + return 1;
> + }
> + ret = lgfs2_lang_parsef(state, src);
> + fclose(src);
> + free(str);
> + if (ret != 0 || state->ls_errnum != 0) {
> + return 1;
> + }
> + return 0;
> +}
> diff --git a/gfs2/libgfs2/super.c b/gfs2/libgfs2/super.c
> index a3c1964..fdf0e60 100644
> --- a/gfs2/libgfs2/super.c
> +++ b/gfs2/libgfs2/super.c
> @@ -25,7 +25,7 @@ int check_sb(struct gfs2_sb *sb)
> {
> if (sb->sb_header.mh_magic != GFS2_MAGIC ||
> sb->sb_header.mh_type != GFS2_METATYPE_SB) {
> - errno = -EIO;
> + errno = EIO;
> return -1;
> }
> if (sb->sb_fs_format == GFS_FORMAT_FS &&
More information about the Cluster-devel
mailing list