diff --git a/src/brp/Makefile b/src/brp/Makefile index 37b0cc2..790a97c 100644 --- a/src/brp/Makefile +++ b/src/brp/Makefile @@ -1,5 +1,5 @@ -all: brp.c - $(CC) -Wall -static brp.c -o brp -lfuse -lbedrock +all: brp.c parser.c + $(CC) -Wall -static brp.c parser.c -o brp -lfuse -lbedrock clean: -rm brp diff --git a/src/brp/brp.c b/src/brp/brp.c index 0feaedc..7519c1d 100644 --- a/src/brp/brp.c +++ b/src/brp/brp.c @@ -34,7 +34,7 @@ #define STRATA_ROOT "/bedrock/strata/" #define STRATA_ROOT_LEN strlen(STRATA_ROOT) -#define MIN(x,y) (x < y ? x : y) +#define MIN(x, y) (x < y ? x : y) enum filter { FILTER_PASS, /* pass file through unaltered */ @@ -52,15 +52,13 @@ enum file_type { * Possible input source for a file. */ struct in_item { - /* full path including STRATA_ROOT, e.g. "/bedrock/strata/gentoo/bin/ls" */ - char *full_path; - size_t full_path_len; /* stratum-specific component of path, e.g. "/bin/ls" */ - char *stratum_path; - size_t stratum_path_len; - /* stratum which provides file, e.g. 'gentoo" */ - char *stratum; - size_t stratum_len; + char *path; + size_t path_len; + + /* if this in_item has a stratum specification, it's + * stored here, otherwise this is -1 */ + int stratum_id; }; /* @@ -79,6 +77,21 @@ struct out_item { size_t in_item_count; }; +static char **stratum; +static size_t *stratum_len; +static int nstratum; + +#include "parser.h" + +#define CONFIG "/bedrock/etc/brp.conf" +#define CONFIG_LEN strlen(CONFIG) +#define STRATA_ROOT "/bedrock/strata/" +#define STRATA_ROOT_LEN strlen(STRATA_ROOT) + +#define MIN(x, y) (x < y ? x : y) +#define unlikely(x) \ + (__builtin_constant_p(x) ? !!(x) : __builtin_expect(!!(x), 0)) + /* * The functions corresponding to the various filesystem calls have * FUSE-defined arguments which are not easily changed. Easiest/cleanest way to pass @@ -105,21 +118,39 @@ void free_config() return; } - int i, j; + size_t i, j; for (i = 0; i < out_item_count; i++) { - for (j = 0; j < out_items[i].in_item_count; j++) { - free(out_items[i].in_items[j].full_path); - free(out_items[i].in_items[j].stratum_path); - free(out_items[i].in_items[j].stratum); - } + for (j = 0; j < out_items[i].in_item_count; j++) + free(out_items[i].in_items[j].path); free(out_items[i].in_items); free(out_items[i].path); } free(out_items); + free(stratum); + free(stratum_len); + nstratum = 0; out_item_count = 0; } -void parse_config() +static inline void _add_stratum(char ***stratum_p, size_t *arrsz, + const char *new_stratum) +{ + size_t s = 0; + while ((*stratum_p)[s] != NULL) { + if (strcmp((*stratum_p)[s], new_stratum) == 0) + return; + s++; + } + + if (s >= *arrsz - 1) { + *arrsz *= 2; + *stratum_p = realloc(*stratum_p, *arrsz * sizeof(void *)); + } + (*stratum_p)[s] = strdup(new_stratum); + (*stratum_p)[s + 1] = NULL; +} + +void brp_parse_config() { /* * Free memory associated with previous config parsing @@ -127,206 +158,149 @@ void parse_config() free_config(); /* - * Ensure we're using a root-modifiable-only configuration file, just in case. + * Ensure we're using a root-modifiable-only configuration file, just in + * case. */ if (!check_config_secure(CONFIG)) { - fprintf(stderr, "brp: config file at "CONFIG" is not secure, refusing to continue.\n"); + fprintf(stderr, "brp: config file at " CONFIG + " is not secure, refusing to continue.\n"); exit(1); } - /* - * Pre-parse config with awk to simplify C parsing code. - */ + int nsec; + struct section *sec = parse_config(CONFIG, &nsec), *sec_curr = sec; - FILE* fp = popen( - "/bedrock/libexec/busybox awk '\n" - "BEGIN {\n" - " FS=\"[=, ]\\+\"\n" - "\n" - " # get enabled strata\n" - " cmd=\"/bedrock/bin/bri -l\"\n" - " while (cmd | getline) {\n" - " existing_strata[$0] = $0\n" - " }\n" - " close(cmd)\n" - "}\n" - "\n" - "/^\\s*#/ || /^\\s*;/ || /^\\s*$/ {\n" - " # empty line or comment, skip\n" - " next\n" - "}\n" - "\n" - "length($0) > max_line_len {\n" - " max_line_len = length($0)\n" - "}\n" - "\n" - "/^\\s*\\[[^]]*\\]\\s*$/ {\n" - " # section header\n" - " section = substr($1, 2, length($1)-2)\n" - " next\n" - "}\n" - "\n" - "section == \"stratum-order\" {\n" - " if ($0 in existing_strata && !($0 in strata)) {\n" - " strata_ordered[stratum_count++] = $0\n" - " strata[$0] = $0\n" - " }\n" - " next\n" - "}\n" - "\n" - "section == \"pass\" || section == \"brc-wrap\" || section == \"exec-filter\" {\n" - " item_count+=0; # ensure is a integer, not a string\n" - " if (substr($1, length($1)) != \"/\") {\n" - " items[item_count\".path\"] = $1\n" - " items[item_count\".type\"] = \"normal\"\n" - " } else {\n" - " items[item_count\".path\"] = substr($1, 1, length($1)-1)\n" - " items[item_count\".type\"] = \"directory\"\n" - " }\n" - "\n" - " items[item_count\".filter\"] = section\n" - "\n" - " items[item_count\".in_count\"] = NF - 1\n" - " \n" - " for (i=2; i <= NF; i++) {\n" - " if ( index($i, \":\") == 0) {\n" - " items[item_count\".in.\"(i-2)\".stratum\"] = \"\"\n" - " items[item_count\".in.\"(i-2)\".path\"] = $i\n" - " } else {\n" - " items[item_count\".in.\"(i-2)\".stratum\"] = substr($i, 0, index($i, \":\")-1)\n" - " items[item_count\".in.\"(i-2)\".path\"] = substr($i, index($i, \":\")+1)\n" - " }\n" - " }\n" - "\n" - " item_count++;\n" - "}\n" - "\n" - "END {\n" - " for (stratum in existing_strata) {\n" - " if (!(stratum in strata)) {\n" - " strata_ordered[stratum_count++] = stratum\n" - " }\n" - " }\n" - "\n" - " print max_line_len\n" - " print item_count\n" - "\n" - " for (item_i = 0; item_i < item_count; item_i++) {\n" - " print items[item_i\".path\"]\n" - " print items[item_i\".type\"]\n" - " print items[item_i\".filter\"]\n" - " in_count = 0\n" - " for (in_i = 0; in_i < items[item_i\".in_count\"]; in_i++) {\n" - " if (items[item_i\".in.\"in_i\".stratum\"] != \"\") {\n" - " in_count++\n" - " } else {\n" - " in_count+=stratum_count\n" - " }\n" - " }\n" - " print in_count\n" - " for (in_i = 0; in_i < items[item_i\".in_count\"]; in_i++) {\n" - " if (items[item_i\".in.\"in_i\".stratum\"] != \"\") {\n" - " print items[item_i\".in.\"in_i\".stratum\"]\n" - " print items[item_i\".in.\"in_i\".path\"]\n" - " }\n" - " }\n" - " for (stratum_i = 0; stratum_i < stratum_count; stratum_i++) {\n" - " for (in_i = 0; in_i < items[item_i\".in_count\"]; in_i++) {\n" - " if (items[item_i\".in.\"in_i\".stratum\"] == \"\") {\n" - " print strata_ordered[stratum_i]\n" - " print items[item_i\".in.\"in_i\".path\"]\n" - " }\n" - " }\n" - " }\n" - " }\n" - "}\n" - "' " CONFIG, "r"); - - if (fp == NULL) { - fprintf(stderr, "brp: Failed to parse config\n"); + if (sec == NULL) exit(1); + + size_t arrsz = 0; + while (sec_curr) { + if (strcmp(sec_curr->name, "stratum-order")) + out_item_count += sec_curr->nent; + else { + arrsz = sec_curr->nent + 1; + stratum = calloc(sec_curr->nent + 1, sizeof(void *)); + + struct entry *e = sec_curr->e; + while (e) { + _add_stratum(&stratum, &arrsz, e->lhs); + e = e->next; + } + } + sec_curr = sec_curr->next; + } + + struct dirent *dent; + DIR *d = opendir("/bedrock/run/enabled_strata/"); + if (!d) { + perror("Failed to list enabled strata"); + exit(1); + } + + if (!arrsz) { + arrsz = 1; + stratum = calloc(1, sizeof(void *)); + } + while ((dent = readdir(d))) + if (strncmp(dent->d_name, ".", 1) && + strncmp(dent->d_name, "..", 2)) + _add_stratum(&stratum, &arrsz, dent->d_name); + + closedir(d); + + char **tmp_s = stratum; + nstratum = 0; + while (*tmp_s) { + nstratum++; + tmp_s++; } - /* malloc to hold one line */ - int maxlinelen; - fscanf(fp, "%d\n", &maxlinelen); - char* line = malloc(maxlinelen * sizeof(char)); + stratum_len = malloc(sizeof(size_t) * nstratum); - /* get items */ - fgets(line, maxlinelen, fp); - sscanf(line, "%ld", &out_item_count); + for (int st = 0; st < nstratum; st++) + stratum_len[st] = strlen(stratum[st]); out_items = malloc(out_item_count * sizeof(struct out_item)); - int i, j; - for (i=0; i < out_item_count; i++) { - /* get path */ - fgets(line, maxlinelen, fp); - line[strlen(line)-1] = '\0'; /* strip newline */ - out_items[i].path = malloc((strlen(line)+1) * sizeof(char)); - strcpy(out_items[i].path, line); - out_items[i].path_len = strlen(line); - - /* get type */ - fgets(line, maxlinelen, fp); - line[strlen(line)-1] = '\0'; /* strip newline */ - if (strcmp(line, "normal") == 0) { - out_items[i].file_type = FILE_TYPE_NORMAL; - } else if (strcmp(line, "directory") == 0) { - out_items[i].file_type = FILE_TYPE_DIRECTORY; - } else { - fprintf(stderr, "brp: Failed to parse config\n"); - exit(1); - } + sec_curr = sec; - /* get filter */ - fgets(line, maxlinelen, fp); - line[strlen(line)-1] = '\0'; /* strip newline */ - if (strcmp(line, "pass") == 0) { - out_items[i].filter = FILTER_PASS; - } else if (strcmp(line, "brc-wrap") == 0) { - out_items[i].filter = FILTER_BRC_WRAP; - } else if (strcmp(line, "exec-filter") == 0) { - out_items[i].filter = FILTER_EXEC; + int i = 0; + int curr_filter = 0; + while (sec_curr) { + struct entry *e = sec_curr->e; + if (strcmp(sec_curr->name, "pass") == 0) { + curr_filter = FILTER_PASS; + } else if (strcmp(sec_curr->name, "brc-wrap") == 0) { + curr_filter = FILTER_BRC_WRAP; + } else if (strcmp(sec_curr->name, "exec-filter") == 0) { + curr_filter = FILTER_EXEC; + } else if (strcmp(sec_curr->name, "stratum-order") == 0) { + sec_curr = sec_curr->next; + continue; } else { fprintf(stderr, "brp: Failed to parse config\n"); exit(1); } + while (e) { + /* get path */ + out_items[i].path = strdup(e->lhs); + out_items[i].path_len = e->lhs_len; + + if (out_items[i].path[out_items[i].path_len - 1] == + '/') { + out_items[i].file_type = FILE_TYPE_DIRECTORY; + out_items[i].path[--out_items[i].path_len] = + '\0'; + } else + out_items[i].file_type = FILE_TYPE_NORMAL; + + out_items[i].filter = curr_filter; + + out_items[i].in_items = + malloc(e->nrhs * sizeof(struct in_item)); + + struct rhs *r = e->r; + struct in_item *in_items = out_items[i].in_items; + int j = 0; + while (r) { + in_items[j].stratum_id = -1; + if (r->str[0] != '/') { + // The in_path might start with a + // stratum + // specification + char *colon = strchr(r->str, ':'); + if (!colon || colon[1] != '/') { + // Invalid path, just ignore + r = r->next; + continue; + } + in_items[j].path = strdup(colon + 1); + for (int st = 0; st < nstratum; st++) + if (strncmp(r->str, stratum[st], + colon - r->str) == + 0) { + in_items[j].stratum_id = + st; + break; + } + if (in_items[j].stratum_id == -1) { + r = r->next; + continue; + } + } else + in_items[j].path = strdup(r->str); + in_items[j].path_len = strlen(in_items[j].path); + j++; - /* get input items */ - fgets(line, maxlinelen, fp); - sscanf(line, "%ld", &out_items[i].in_item_count); - out_items[i].in_items = malloc(out_items[i].in_item_count * sizeof(struct in_item)); - for (j=0; j < out_items[i].in_item_count; j++) { - /* get stratum */ - fgets(line, maxlinelen, fp); - line[strlen(line)-1] = '\0'; /* strip newline */ - out_items[i].in_items[j].stratum = malloc((strlen(line)+1) * sizeof(char)); - strcpy(out_items[i].in_items[j].stratum, line); - out_items[i].in_items[j].stratum_len = strlen(line); - - /* get stratum path */ - fgets(line, maxlinelen, fp); - line[strlen(line)-1] = '\0'; /* strip newline */ - if (line[strlen(line)-1] == '/') { - line[strlen(line)-1] = '\0'; /* strip trailing slash */ + r = r->next; } - out_items[i].in_items[j].stratum_path = malloc((strlen(line)+1) * sizeof(char)); - strcpy(out_items[i].in_items[j].stratum_path, line); - out_items[i].in_items[j].stratum_path_len = strlen(line); - - /* calculate full path */ - out_items[i].in_items[j].full_path = malloc(( - STRATA_ROOT_LEN + - out_items[i].in_items[j].stratum_len + - out_items[i].in_items[j].stratum_path_len + 1) * sizeof(char)); - strcpy(out_items[i].in_items[j].full_path, STRATA_ROOT); - strcat(out_items[i].in_items[j].full_path, out_items[i].in_items[j].stratum); - strcat(out_items[i].in_items[j].full_path, out_items[i].in_items[j].stratum_path); + out_items[i].in_item_count = j; + i++; + e = e->next; } + sec_curr = sec_curr->next; } - free(line); - pclose(fp); + free_sections(sec); } /* @@ -334,12 +308,10 @@ void parse_config() * the calling program to free() it. This is used when /reparse_config is read * to show the current configuration. It is useful for debugging. */ -char* config_contents() +char *config_contents() { - int i, j; - size_t len = 0; - for (i = 0; i < out_item_count; i++) { + for (size_t i = 0; i < out_item_count; i++) { len += strlen("path = "); len += strlen(out_items[i].path); len += strlen("\n"); @@ -369,27 +341,31 @@ char* config_contents() } len += strlen("\n"); - for (j = 0; j < out_items[i].in_item_count; j++) { - len += strlen(" stratum = "); - len += strlen(out_items[i].in_items[j].stratum); - len += strlen("\n"); - len += strlen(" stratum_path = "); - len += strlen(out_items[i].in_items[j].stratum_path); - len += strlen("\n"); - len += strlen(" full_path = "); - len += strlen(out_items[i].in_items[j].full_path); - len += strlen("\n"); + for (size_t j = 0; j < out_items[i].in_item_count; j++) { + for (int st = 0; st < nstratum; st++) { + len += strlen(" stratum = "); + len += strlen(stratum[st]); + len += strlen("\n"); + len += strlen(" stratum_path = "); + len += strlen(stratum[st]) + STRATA_ROOT_LEN; + len += strlen("\n"); + len += strlen(" full_path = "); + len += strlen(stratum[st]) + + out_items[i].in_items[j].path_len + + STRATA_ROOT_LEN; + len += strlen("\n"); + } } } len += strlen("\n"); - char *config_str = malloc((len+1) * sizeof(char)); + char *config_str = malloc((len + 1) * sizeof(char)); if (!config_str) { return NULL; } config_str[0] = '\0'; - for (i = 0; i < out_item_count; i++) { + for (size_t i = 0; i < out_item_count; i++) { strcat(config_str, "path = "); strcat(config_str, out_items[i].path); strcat(config_str, "\n"); @@ -419,19 +395,23 @@ char* config_contents() } strcat(config_str, "\n"); - for (j = 0; j < out_items[i].in_item_count; j++) { - strcat(config_str, " stratum = "); - strcat(config_str, out_items[i].in_items[j].stratum); - strcat(config_str, "\n"); - strcat(config_str, " stratum_path = "); - strcat(config_str, out_items[i].in_items[j].stratum_path); - strcat(config_str, "\n"); - strcat(config_str, " full_path = "); - strcat(config_str, out_items[i].in_items[j].full_path); - strcat(config_str, "\n"); + for (size_t j = 0; j < out_items[i].in_item_count; j++) { + for (int st = 0; st < nstratum; st++) { + strcat(config_str, " stratum = "); + strcat(config_str, stratum[st]); + strcat(config_str, "\n"); + strcat(config_str, " stratum_path = "); + strcat(config_str, STRATA_ROOT); + strcat(config_str, stratum[st]); + strcat(config_str, "\n"); + strcat(config_str, " full_path = "); + strcat(config_str, STRATA_ROOT); + strcat(config_str, stratum[st]); + strcat(config_str, out_items[i].in_items[j].path); + strcat(config_str, "\n"); + } } } - len += strlen("\n"); return config_str; } @@ -557,7 +537,7 @@ int str_vec_uniq(struct str_vec *v) * - Do not use trailing null; track offset into buffer instead * - Able to skip set number of input bytes before writing into buffer */ -void strcatoffset(char *buf, const char const *str, size_t *left_to_skip, size_t *written, size_t max) +void strcatoffset(char *buf, const char *str, size_t *left_to_skip, size_t *written, size_t max) { size_t str_len = strlen(str); int i = 0; @@ -595,7 +575,7 @@ int write_attempt(const char* path) /* Non-root users cannot do anything with this file. */ return -EACCES; } else { - parse_config(); + brp_parse_config(); return 0; } } else { @@ -603,241 +583,194 @@ int write_attempt(const char* path) } } +static int brp_orig_cwd, brp_orig_root; + /* - * Determines the real path to a file in a strata, treating absolute symlinks - * as symlinks relative to the strata's root. Returns >=0 on success, <0 on - * failure. If there is no resulting file/directory it is considered an error - * (unlike, for example, readlink(2)). + * Given an input path, finds the corresponding content to output (if any) and + * populates various related fields (e.g. stat info) accordingly. * - * All incoming paths must be in STRATA_ROOT//. + * Returns 0 if found, -ENOENT indicates not found * - * This is used over something like realpath() due to the need to have absolute - * symlinks start at the symlink's stratum's root. e.g. if a symlink at - * "/bedrock/strata/foo/bar" symlinks to "/qux" that should be treated as - * "/bedrock/strata/foo/qux". + * If out_fd is not NULL, the file will be opened, and file descriptor assigned. */ -int brp_realpath(char *in_path, char *out_path, size_t bufsize) +int corresponding(char *in_path, int *out_fd, struct stat *stbuf, + struct out_item **arg_out_item, int *stratum_id, + struct in_item **arg_in_item, char **tail) { - if (strlen(in_path) >= bufsize) { - return -ENAMETOOLONG; + /* handle root specially */ + if (in_path[0] == '/' && in_path[1] == '\0') { + memcpy(stbuf, &parent_stat, sizeof(parent_stat)); + return 0; } - char *slash; - const int LOOP_MAX = 20; - int end = 0; - int loop = 0; - size_t offset; - ssize_t readlink_len; - struct stat stbuf; + int retval = -ENOENT; - char current_path[bufsize]; - char link_path[bufsize]; - char path_fragment[bufsize]; - char stratum_prefix[bufsize]; + size_t i, j; + size_t in_path_len = strlen(in_path); + int st; + char tmp_path[PATH_MAX + 1]; + int *stratum_root_fd = malloc(sizeof(int) * nstratum); - /* - * Ensure incoming path is in stratum_prefix//, get stratum - * prefix. - */ - if (strncmp(in_path, STRATA_ROOT, STRATA_ROOT_LEN) != 0) { - return -EINVAL; + int ret = seteuid(0); + if (unlikely(ret < 0)) { + perror("seteuid"); + exit(1); } - slash = in_path + STRATA_ROOT_LEN + 1; - while (*slash != '/') { - slash++; + + ret = chdir(STRATA_ROOT); + if (unlikely(ret < 0)) { + // strata root missing?! + perror("Failed to chdir to strata root"); + exit(1); } - strncpy(stratum_prefix, in_path, slash - in_path + 1); - stratum_prefix[slash - in_path + 1] = '\0'; - size_t stratum_prefix_len = strlen(stratum_prefix); - offset = stratum_prefix_len+1; - strcpy(current_path, in_path); + for (st = 0; st < nstratum; st++) + stratum_root_fd[st] = open(stratum[st], O_RDONLY | O_DIRECTORY); - /* - * Loop over every directory in a given file path, e.g. in - * "/foo/bar/baz/" loop over "/foo" then "/foo/bar" then - * "/foo/bar/baz", starting with the stratum_prefix. If any directory - * is found to be a symlink, resolve symlink then start over. - */ - while (1) { - /* find next file/directory in file path */ - while (current_path[offset] != '/' && current_path[offset] != '\0') { - offset++; - } - /* on final item in file path, the file itself */ - if (current_path[offset] == '\0') { - end = 1; - } - /* starting / */ - if (offset == 0 && current_path[offset] == '/') { - offset++; + for (st = 0; st < nstratum; st++) { + if (unlikely(stratum_root_fd[st] < 0)) continue; - } - /* would overflow */ - if (offset >= bufsize) { - return -ENAMETOOLONG; - } - - strncpy(path_fragment, current_path, offset); - path_fragment[offset] = '\0'; - if (lstat(path_fragment, &stbuf) != 0) { - return -ENOENT; - } - if (!S_ISLNK(stbuf.st_mode) && !end) { - offset++; + ret = fchdir(stratum_root_fd[st]); + if (unlikely(ret < 0)) + continue; + ret = chroot("."); + if (unlikely(ret < 0)) continue; - } - if (!S_ISLNK(stbuf.st_mode) && end) { - strcpy(out_path, current_path); - return 1; - } - if (S_ISLNK(stbuf.st_mode)) { - if ((readlink_len = readlink(path_fragment, link_path, bufsize)) < 0) { - return -errno; - } - link_path[readlink_len] = '\0'; - if (link_path[0] == '/') { - /* absolute symlink */ - strcpy(out_path, stratum_prefix); - strcat(out_path, link_path+1); - strcat(out_path, current_path + offset); - } else { - /* relative symlink */ - if ( (slash = strrchr(path_fragment, '/')) ) { - strncpy(out_path, path_fragment, slash - path_fragment + 1); - out_path[slash - path_fragment + 1] = '\0'; - } else { - strcpy(out_path, "./"); - } - strcat(out_path, link_path); - strcat(out_path, current_path + offset); - } - offset = stratum_prefix_len+1; - end = 0; - strcpy(current_path, out_path); - if ((loop++) >= LOOP_MAX) { - return -ELOOP; - } - } - } -} -/* - * Like stat(2), except resolves symlinks with brp-specific resolve_symlink() - * logic. - */ -int brp_stat(char *path, struct stat *stbuf) -{ - char out_path[PATH_MAX+1]; - int ret; - if ((ret = brp_realpath(path, out_path, PATH_MAX+1)) < 0) { - /* could not resolve symlink */ - return ret; - } else if (!stbuf) { - /* no stbuf provided to populate */ - return 0; - } else { - return lstat(out_path, stbuf); - } -} + /* check for a match on something contained in one of the configured + * directories */ + for (i = 0; i < out_item_count; i++) { + if (strncmp(in_path, out_items[i].path, + out_items[i].path_len) || + in_path[out_items[i].path_len] != '/' || + out_items[i].file_type != FILE_TYPE_DIRECTORY) + continue; + struct in_item *in_item = out_items[i].in_items; + for (j = 0; j < out_items[i].in_item_count; j++) { + if (in_item[j].stratum_id >= 0 && in_item[j].stratum_id != st) + continue; -/* - * Given an input path, finds the corresponding content to output (if any) and - * populates various related fields (e.g. stat info) accordingly. - */ -int corresponding(char *in_path, - char* out_path, - size_t out_path_size, - struct stat *stbuf, - struct out_item **arg_out_item, - struct in_item **arg_in_item, - char **tail) -{ - /* handle root specially */ - if (in_path[0] == '/' && in_path[1] == '\0') { - memcpy(stbuf, &parent_stat, sizeof(parent_stat)); - return 0; - } + if (unlikely(in_item[j].path_len+in_path_len-out_items[i].path_len > PATH_MAX)) + continue; + strcpy(tmp_path, in_item[j].path); + strcat(tmp_path, in_path + out_items[i].path_len); - size_t i, j; - size_t in_path_len = strlen(in_path); - char tmp_path[out_path_size+1]; + ret = stat(tmp_path, stbuf); + if (ret < 0) + continue; - /* check for a match on something contained in one of the configured - * directories */ - for (i = 0; i < out_item_count; i++) { - if (strncmp(in_path, out_items[i].path, out_items[i].path_len) == 0 && - in_path[out_items[i].path_len] == '/' && - out_items[i].file_type == FILE_TYPE_DIRECTORY) { - for (j = 0; j < out_items[i].in_item_count; j++) { - strncpy(tmp_path, out_items[i].in_items[j].full_path, out_path_size); - strncat(tmp_path, in_path + out_items[i].path_len, out_path_size - out_items[i].in_items[j].full_path_len); - if (strlen(tmp_path) == out_path_size) { - /* would have buffer overflowed, treat as bad value */ + // Check again with proper permission + SET_CALLER_UID(); + ret = stat(tmp_path, stbuf); + if (unlikely(ret < 0)) { + ret = seteuid(0); + if (unlikely(ret < 0)) { + perror("seteuid"); + exit(1); + } continue; } - if (brp_realpath(tmp_path, out_path, out_path_size) >= 0 && lstat(out_path, stbuf) >= 0) { - *arg_out_item = &out_items[i]; - *arg_in_item = &out_items[i].in_items[j]; - *tail = in_path + out_items[i].path_len; - return 0; - } + + *arg_out_item = &out_items[i]; + *arg_in_item = &out_items[i].in_items[j]; + *stratum_id = st; + *tail = in_path + out_items[i].path_len; + + if (out_fd) + *out_fd = open(tmp_path, O_RDONLY); + retval = 0; + goto end; } } - } - /* - * Check for a match directly on one of the configured items. - */ - for (i = 0; i < out_item_count; i++) { - if (strncmp(out_items[i].path, in_path, in_path_len) == 0 && - (out_items[i].path[in_path_len] == '\0')) { + /* + * Check for a match on a virtual parent directory of a + * configured item. + */ + for (i = 0; i < out_item_count; i++) { + if (strncmp(out_items[i].path, in_path, in_path_len) || + (out_items[i].path[in_path_len] != '/' && + out_items[i].path[in_path_len] != '\0')) + continue; + + struct in_item *in_item = out_items[i].in_items; for (j = 0; j < out_items[i].in_item_count; j++) { - if (brp_realpath(out_items[i].in_items[j].full_path, out_path, out_path_size) >=0) { - if (out_items[i].file_type == FILE_TYPE_DIRECTORY) { - memcpy(stbuf, &parent_stat, sizeof(parent_stat)); - } else { - lstat(out_path, stbuf); + if (in_item[j].stratum_id >= 0 && in_item[j].stratum_id != st) + continue; + + int ret = stat(in_item[j].path, stbuf); + if (ret < 0) + continue; + + // Check again with proper permission + SET_CALLER_UID(); + ret = stat(in_item[j].path, stbuf); + if (unlikely(ret < 0)) { + ret = seteuid(0); + if (unlikely(ret < 0)) { + perror("seteuid"); + exit(1); } - *arg_out_item = &out_items[i]; - *arg_in_item = &out_items[i].in_items[j]; - *tail = ""; - return 0; + continue; } + *arg_out_item = &out_items[i]; + *arg_in_item = &out_items[i].in_items[j]; + *stratum_id = st; + *tail = ""; + if (out_items[i].path[in_path_len] == '/' || + out_items[i].file_type == FILE_TYPE_DIRECTORY) + memcpy(stbuf, &parent_stat, sizeof(parent_stat)); + if (out_fd) + *out_fd = open(tmp_path, O_RDONLY); + retval = 0; + goto end; } } } - /* - * Check for a match on a virtual parent directory of a configured - * item. - */ - for (i = 0; i < out_item_count; i++) { - if (strncmp(out_items[i].path, in_path, in_path_len) == 0 && - (out_items[i].path[in_path_len] == '/')) { - for (j = 0; j < out_items[i].in_item_count; j++) { - if (brp_realpath(out_items[i].in_items[j].full_path, out_path, out_path_size) >=0 && - lstat(out_path, stbuf) >= 0) { - memcpy(stbuf, &parent_stat, sizeof(parent_stat)); - *arg_out_item = &out_items[i]; - *arg_in_item = &out_items[i].in_items[j]; - *tail = ""; - return 0; - } - } - } +end: + for (st = 0; st < nstratum; st++) + if (stratum_root_fd[st] > 0) + close(stratum_root_fd[st]); + + free(stratum_root_fd); + + // If anything goes wrong here, we are stuck in a different root. + // In that case we give up and quit. + ret = seteuid(0); + if (unlikely(ret < 0)) { + perror("seteuid"); + exit(1); + } + ret = fchdir(brp_orig_root); + if (unlikely(ret < 0)) { + perror("Failed to chdir to original root"); + exit(1); } - return -ENOENT; + ret = chroot("."); + if (unlikely(ret < 0)) { + perror("Failed to chroot to original root"); + exit(1); + } + + ret = fchdir(brp_orig_cwd); + if (unlikely(ret < 0)) { + perror("Failed to change back to old cwd"); + exit(1); + } + + return retval; } /* * Apply relevant filter to getattr output. */ void stat_filter(struct stat *stbuf, - const char const *in_path, + int in_fd, int filter, + int stratum_id, struct in_item *item, const char *tail) { @@ -851,6 +784,7 @@ void stat_filter(struct stat *stbuf, if (S_ISDIR(stbuf->st_mode)) { /* filters below only touch files */ + close(in_fd); return; } @@ -864,15 +798,15 @@ void stat_filter(struct stat *stbuf, case FILTER_BRC_WRAP: stbuf->st_size = strlen("#!/bedrock/libexec/busybox sh\nexec /bedrock/bin/brc ") - + item->stratum_len + + stratum_len[stratum_id] + strlen(" ") - + item->stratum_path_len + + item->path_len + strlen(tail) + strlen(" \"$@\"\n"); break; case FILTER_EXEC: - fp = fopen(in_path, "r"); + fp = fdopen(in_fd, "r"); if (fp != NULL) { while (fgets(line, PATH_MAX, fp) != NULL) { if (strncmp(line, "Exec=", strlen("Exec=")) == 0 || @@ -881,7 +815,7 @@ void stat_filter(struct stat *stbuf, strncmp(line, "ExecStop=", strlen("ExecStop=")) == 0 || strncmp(line, "ExecReload=", strlen("ExecReload=")) == 0) { stbuf->st_size += strlen("/bedrock/bin/brc "); - stbuf->st_size += item->stratum_len; + stbuf->st_size += stratum_len[stratum_id]; stbuf->st_size += strlen(" "); } } @@ -890,13 +824,15 @@ void stat_filter(struct stat *stbuf, break; } + close(in_fd); } /* * Do read() and apply relevant filter. */ -int read_filter(const char *in_path, +int read_filter(int in_fd, int filter, + int stratum_id, struct in_item *item, const char *tail, char *buf, @@ -904,8 +840,8 @@ int read_filter(const char *in_path, off_t offset) { char *execs[] = {"TryExec=", "ExecStart=", "ExecStop=", "ExecReload=", "Exec="}; - size_t exec_cnt = sizeof(execs) / sizeof(execs[0]); - int fd, ret; + size_t exec_cnt = sizeof(execs) / sizeof(execs[0]), left_to_skip, written; + int ret; const size_t line_max = PATH_MAX; char line[line_max+1]; FILE *fp; @@ -913,64 +849,54 @@ int read_filter(const char *in_path, switch (filter) { case FILTER_PASS: - if ((fd = open(in_path, O_RDONLY)) >= 0) { - ret = pread(fd, buf, size, offset); - close(fd); - return ret; - } - return fd; - break; + ret = pread(in_fd, buf, size, offset); + close(in_fd); + return ret; case FILTER_BRC_WRAP: - if (access(in_path, F_OK) == 0) { - size_t left_to_skip = offset; - size_t written = 0; - strcatoffset(buf, "#!/bedrock/libexec/busybox sh\nexec /bedrock/bin/brc ", &left_to_skip, &written, size); - strcatoffset(buf, item->stratum, &left_to_skip, &written, size); - strcatoffset(buf, " ", &left_to_skip, &written, size); - strcatoffset(buf, item->stratum_path, &left_to_skip, &written, size); - strcatoffset(buf, tail, &left_to_skip, &written, size); - strcatoffset(buf, " \"$@\"\n", &left_to_skip, &written, size); - return written; - } else { - return -EPERM; - } - break; + close(in_fd); + + left_to_skip = offset; + written = 0; + strcatoffset(buf, "#!/bedrock/libexec/busybox sh\nexec /bedrock/bin/brc ", &left_to_skip, &written, size); + strcatoffset(buf, stratum[stratum_id], &left_to_skip, &written, size); + strcatoffset(buf, " ", &left_to_skip, &written, size); + strcatoffset(buf, item->path, &left_to_skip, &written, size); + strcatoffset(buf, tail, &left_to_skip, &written, size); + strcatoffset(buf, " \"$@\"\n", &left_to_skip, &written, size); + return written; case FILTER_EXEC: - if (access(in_path, F_OK) == 0) { - size_t left_to_skip = offset; - size_t written = 0; - fp = fopen(in_path, "r"); - if (!fp) { - return -errno; - } - while (fgets(line, line_max, fp) != NULL) { - size_t i; - int found = 0; - for (i = 0; i < exec_cnt; i++) { - if (strncmp(line, execs[i], strlen(execs[i])) == 0) { - found = 1; - strcatoffset(buf, execs[i], &left_to_skip, &written, size); - strcatoffset(buf, "/bedrock/bin/brc ", &left_to_skip, &written, size); - strcatoffset(buf, item->stratum, &left_to_skip, &written, size); - strcatoffset(buf, " ", &left_to_skip, &written, size); - strcatoffset(buf, line + strlen(execs[i]), &left_to_skip, &written, size); - } - } - if (!found) { - strcatoffset(buf, line, &left_to_skip, &written, size); - } - if (written >= size) { - break; + left_to_skip = offset; + written = 0; + fp = fdopen(in_fd, "r"); + if (!fp) { + int ret = errno; + close(in_fd); + return -ret; + } + while (fgets(line, line_max, fp) != NULL) { + size_t i; + int found = 0; + for (i = 0; i < exec_cnt; i++) { + if (strncmp(line, execs[i], strlen(execs[i])) == 0) { + found = 1; + strcatoffset(buf, execs[i], &left_to_skip, &written, size); + strcatoffset(buf, "/bedrock/bin/brc ", &left_to_skip, &written, size); + strcatoffset(buf, stratum[stratum_id], &left_to_skip, &written, size); + strcatoffset(buf, " ", &left_to_skip, &written, size); + strcatoffset(buf, line + strlen(execs[i]), &left_to_skip, &written, size); } } - fclose(fp); - return written; - } else { - return -EPERM; + if (!found) { + strcatoffset(buf, line, &left_to_skip, &written, size); + } + if (written >= size) { + break; + } } - break; + fclose(fp); + return written; } return -ENOENT; @@ -986,16 +912,16 @@ int read_filter(const char *in_path, * FUSE calls its equivalent of stat(2) "getattr". This just gets stat * information, e.g. file size and permissions. */ -static int brp_getattr(const char const *in_path, struct stat *stbuf) +static int brp_getattr(const char *in_path, struct stat *stbuf) { SET_CALLER_UID(); - char out_path[PATH_MAX+1]; struct out_item *out_item; struct in_item *in_item; char *tail; char *config_str; - int ret; + int ret, fd; + int stratum_id; if (in_path[0] == '/' && in_path[1] == '\0') { memcpy(stbuf, &parent_stat, sizeof(parent_stat)); @@ -1014,8 +940,8 @@ static int brp_getattr(const char const *in_path, struct stat *stbuf) } } - if ( (ret = corresponding((char*)in_path, out_path, PATH_MAX, stbuf, &out_item, &in_item, &tail)) >= 0) { - stat_filter(stbuf, out_path, out_item->filter, in_item, tail); + if ( (ret = corresponding((char*)in_path, &fd, stbuf, &out_item, &stratum_id, &in_item, &tail)) >= 0) { + stat_filter(stbuf, fd, out_item->filter, stratum_id, in_item, tail); return 0; } else { return ret; @@ -1029,89 +955,137 @@ static int brp_readdir(const char *in_path, void *buf, fuse_fill_dir_t filler, o { SET_CALLER_UID(); - (void) offset; - (void) fi; + (void)offset; + (void)fi; - const size_t out_path_size = PATH_MAX; - char out_path[out_path_size+1]; + char out_path[PATH_MAX + 1]; size_t i, j; + int st; size_t in_path_len = strlen(in_path); /* handle root directory specially */ if (in_path_len == 1) { in_path_len = 0; } struct stat stbuf; + struct in_item *in_item; int ret_val = -ENOENT; char *slash; + int *stratum_root_fd = malloc(sizeof(int)*nstratum); + + int ret = chdir(STRATA_ROOT); + if (unlikely(ret < 0)) { + // strata root missing?! + perror("Failed to chdir to strata root"); + exit(1); + } + + for (st = 0; st < nstratum; st++) + stratum_root_fd[st] = open(stratum[st], O_RDONLY|O_DIRECTORY); - DIR *d; struct dirent *dir; struct str_vec v; str_vec_new(&v); - for (i = 0; i < out_item_count; i++) { - /* - * Check for contents of one of the configured directories - */ - if (strncmp(in_path, out_items[i].path, out_items[i].path_len) == 0 && - (in_path[out_items[i].path_len] == '\0' || - in_path[out_items[i].path_len] == '/') && - out_items[i].file_type == FILE_TYPE_DIRECTORY) { + for (st = 0; st < nstratum; st++) { + if (unlikely(stratum_root_fd[st] < 0)) + continue; + + ret = seteuid(0); + if (unlikely(ret < 0)) + return -errno; + + ret = fchdir(stratum_root_fd[st]); + if (unlikely(ret < 0)) + continue; + ret = chroot("."); + if (unlikely(ret < 0)) + continue; + + SET_CALLER_UID(); + for (i = 0; i < out_item_count; i++) { + /* + * Check for contents of one of the configured + * directories + */ + in_item = out_items[i].in_items; + if (strncmp(in_path, out_items[i].path, + out_items[i].path_len) || + (in_path[out_items[i].path_len] != '\0' && + in_path[out_items[i].path_len] != '/') || + out_items[i].file_type != FILE_TYPE_DIRECTORY) + continue; for (j = 0; j < out_items[i].in_item_count; j++) { - strncpy(out_path, out_items[i].in_items[j].full_path, out_path_size); - strncat(out_path, in_path + out_items[i].path_len, out_path_size - out_items[i].in_items[j].full_path_len); - if (strlen(out_path) == out_path_size) { - /* would have buffer overflowed, treat as bad value */ + if (in_item[j].stratum_id >= 0 && in_item[j].stratum_id != st) continue; - } - if (brp_stat(out_path, &stbuf) < 0) { + + if (in_item[j].path_len + in_path_len - + out_items[i].path_len > PATH_MAX) continue; - } + + strcpy(out_path, in_item[j].path); + strcat(out_path, in_path + out_items[i].path_len); + + ret = stat(out_path, &stbuf); + if (ret < 0) + continue; + if (S_ISDIR(stbuf.st_mode)) { - if (! (d = opendir(out_path)) ) { + DIR *d = opendir(out_path); + if (!d) { + perror("opendir()"); continue; } - while ( (dir = readdir(d)) ) { + while ( (dir = readdir(d) )) { str_vec_append(&v, dir->d_name); } closedir(d); } else { if (strrchr(out_path, '/')) { - str_vec_append(&v, strrchr(out_path, '/')+1); + str_vec_append(&v, strrchr(out_path, '/') + 1); } else { str_vec_append(&v, out_path); } } } } - /* - * Check for a match directly on one of the configured items or a virtual parent directory - */ - if (strncmp(out_items[i].path, in_path, in_path_len) == 0 && - out_items[i].path[in_path_len] == '/') { + for (i = 0; i < out_item_count; i++) { + /* + * Check for a match directly on one of the configured + * items or a virtual parent directory + */ + if (strncmp(out_items[i].path, in_path, in_path_len) || + out_items[i].path[in_path_len] != '/') + continue; + + in_item = out_items[i].in_items; for (j = 0; j < out_items[i].in_item_count; j++) { - if (brp_stat(out_items[i].in_items[j].full_path, NULL) >= 0) { - strncpy(out_path, out_items[i].path + in_path_len + 1, out_path_size); - if (strlen(out_path) == out_path_size) { - /* would have buffer overflowed, treat as bad value */ - continue; - } - if ( (slash = strchr(out_path, '/')) ) { - *slash = '\0'; - } - str_vec_append(&v, out_path); - break; + if (in_item[j].stratum_id >= 0 && + in_item[j].stratum_id != st) + continue; + + ret = stat(in_item[j].path, &stbuf); + if (ret < 0) + continue; + if (out_items[i].path_len - in_path_len - 1 > + PATH_MAX) + continue; + strcpy(out_path, + out_items[i].path + in_path_len + 1); + if ((slash = strchr(out_path, '/'))) { + *slash = '\0'; } + str_vec_append(&v, out_path); + break; } } + } - /* - * Handle reparse_config on root - */ - if (in_path[0] == '/' && in_path[1] == '\0') { - str_vec_append(&v, "reparse_config"); - } + /* + * Handle reparse_config on root + */ + if (in_path[0] == '/' && in_path[1] == '\0') { + str_vec_append(&v, "reparse_config"); } str_vec_uniq(&v); @@ -1124,6 +1098,37 @@ static int brp_readdir(const char *in_path, void *buf, fuse_fill_dir_t filler, o str_vec_free(&v); + for (st = 0; st < nstratum; st++) + if (stratum_root_fd[st] >= 0) + close(stratum_root_fd[st]); + + free(stratum_root_fd); + + // If anything goes wrong here, we are stuck in a different root. + // In that case we give up and quit. + ret = seteuid(0); + if (unlikely(ret < 0)) { + perror("seteuid"); + exit(1); + } + ret = fchdir(brp_orig_root); + if (unlikely(ret < 0)) { + perror("Failed to chdir to original root"); + exit(1); + } + + ret = chroot("."); + if (unlikely(ret < 0)) { + perror("Failed to chroot to original root"); + exit(1); + } + + ret = fchdir(brp_orig_cwd); + if (unlikely(ret < 0)) { + perror("Failed to change back to old cwd"); + exit(1); + } + return ret_val; } @@ -1134,8 +1139,8 @@ static int brp_open(const char *in_path, struct fuse_file_info *fi) { SET_CALLER_UID(); - char out_path[PATH_MAX+1]; struct out_item *out_item; + int stratum_id; struct in_item *in_item; char *tail; int ret; @@ -1168,7 +1173,7 @@ static int brp_open(const char *in_path, struct fuse_file_info *fi) return -EACCES; } - if ( (ret = corresponding((char*)in_path, out_path, PATH_MAX, &stbuf, &out_item, &in_item, &tail)) >= 0) { + if ( (ret = corresponding((char*)in_path, NULL, &stbuf, &out_item, &stratum_id, &in_item, &tail)) >= 0) { return 0; } return -ENOENT; @@ -1179,15 +1184,16 @@ static int brp_open(const char *in_path, struct fuse_file_info *fi) */ static int brp_read(const char *in_path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + (void)fi; SET_CALLER_UID(); - char out_path[PATH_MAX+1]; struct out_item *out_item; + int stratum_id; struct in_item *in_item; char *tail; char *config_str; struct stat stbuf; - int ret; + int ret, fd; if (strcmp(in_path, "/reparse_config") == 0) { config_str = config_contents(); @@ -1200,12 +1206,12 @@ static int brp_read(const char *in_path, char *buf, size_t size, off_t offset, s return ret; } - ret = corresponding((char*) in_path, out_path, PATH_MAX, &stbuf, &out_item, &in_item, &tail); + ret = corresponding((char*) in_path, &fd, &stbuf, &out_item, &stratum_id, &in_item, &tail); if (ret < 0) { return ret; } - return read_filter(out_path, out_item->filter, in_item, tail, buf, size, offset); + return read_filter(fd, out_item->filter, stratum_id, in_item, tail, buf, size, offset); } /* @@ -1215,6 +1221,9 @@ static int brp_read(const char *in_path, char *buf, size_t size, off_t offset, s */ static int brp_write(const char *in_path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + (void)size; + (void)offset; + (void)fi; SET_CALLER_UID(); if (write_attempt(in_path) == 0) { @@ -1264,11 +1273,23 @@ int main(int argc, char *argv[]) * Ensure we are running as root so that any requests by root to this * filesystem can be provided. */ - if (getuid() != 0){ + if (getuid() != 0) { fprintf(stderr, "ERROR: not running as root, aborting.\n"); return 1; } + brp_orig_cwd = open(".", O_RDONLY|O_DIRECTORY); + if (brp_orig_cwd < 0) { + perror("Failed to open working directory"); + return 1; + } + + brp_orig_root = open("/", O_RDONLY|O_DIRECTORY); + if (brp_orig_root < 0) { + perror("Failed to open root directory"); + return 1; + } + /* * The mount point should be provided. */ @@ -1319,7 +1340,7 @@ int main(int argc, char *argv[]) fuse_opt_add_arg(&args, "-f"); /* initial config parse */ - parse_config(); + brp_parse_config(); return fuse_main(args.argc, args.argv, &brp_oper, NULL); } diff --git a/src/brp/parser.c b/src/brp/parser.c new file mode 100644 index 0000000..4acc8ab --- /dev/null +++ b/src/brp/parser.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include + +#include "parser.h" + +static inline void skip_until_newline(FILE *f, int *line, int *col) { + char *buf = NULL; + size_t n = 0; + getline(&buf, &n, f); + free(buf); + (*line)++; + *col = 0; +} + +static inline void skip_spaces(FILE *f, int *line, int *col) { + int c; + while(isspace(c = fgetc(f)) && c != EOF) { + if (c == '\n') { + (*line)++; + *col = 0; + } else + (*col)++; + } + if (c != EOF) + ungetc(c, f); +} + +struct _buffer { + char *buf; + size_t sz, len; +}; + +static inline void push_char(int c, struct _buffer *buf) { + if (buf->len+1 >= buf->sz) { + buf->sz = buf->sz ? buf->sz*2 : 64; + buf->buf = realloc(buf->buf, buf->sz); + } + buf->buf[buf->len++] = (char)c; +} + +static inline void init_buf(struct _buffer *buf) { + buf->buf = (void *)(buf->sz = buf->len = 0); +} + +static inline char *finalize_buf(struct _buffer *buf) { + char *ret = buf->buf; + if (!ret) + return NULL; + + ret[buf->len] = '\0'; + init_buf(buf); + return ret; +} + +struct section * +parse_config(const char *file, int *_nsec) { + FILE *cfg = fopen(file, "r"); + struct _buffer buf = {0}; + struct section *sec_head = NULL, **sec_next = &sec_head, *sec_curr; + struct entry **e_next = NULL, *e_curr; + struct rhs **rhs_next = NULL, *rhs_curr; + int nsec = 0; + int line = 1, col = 0; + int c; + if (!cfg) + return NULL; + + enum { + LHS, + RHS, + SECTION, + PRELHS, + PRERHS + } state = PRELHS; + do { + c = fgetc(cfg); + if (c == '\n') { + line++; + col = 0; + } else + col++; + //fprintf(stderr, "%d, %d: %c\n", line, col, c); + if (c == '#') { + skip_until_newline(cfg, &line, &col); + c = '\n'; + } + if (isspace(c)) { + if (state == LHS) + goto lhs_end; + else if (state == PRELHS || state == PRERHS) { + skip_spaces(cfg, &line, &col); + continue; + } + } + if (c == EOF) { + handle_eof: + switch(state) { + case PRELHS: case PRERHS: + goto end; + case RHS: + goto rhs_end; + case LHS: + goto lhs_end; + case SECTION: + goto err; + } + } + switch(c) { + case '\\': + c = fgetc(cfg); + if (c == EOF) + goto handle_eof; + if (c == '\n') { + line++; + col = 0; + } else + col++; + goto plain_char; + case '[': + // New section + if (state != PRELHS) + goto plain_char; + state = SECTION; + break; + case ']': + if (state != SECTION) + goto plain_char; + *sec_next = malloc(sizeof(struct section)); + sec_curr = *sec_next; + memset(sec_curr, 0, sizeof(*sec_curr)); + sec_curr->name_len = buf.len; + sec_curr->name = finalize_buf(&buf); + sec_next = &sec_curr->next; + e_next = &sec_curr->e; + skip_spaces(cfg, &line, &col); + state = PRELHS; + nsec++; + break; + case ',': + case '\n': + if (state != RHS) + goto plain_char; + if (!buf.buf) + //Empty rhs, ignore + goto rhs_end; + *rhs_next = malloc(sizeof(struct rhs)); + rhs_curr = *rhs_next; + rhs_curr->next = NULL; + rhs_curr->len = buf.len; + rhs_curr->str = finalize_buf(&buf); + rhs_next = &rhs_curr->next; + e_curr->nrhs++; + skip_spaces(cfg, &line, &col); + rhs_end: + if (c == ',') + break; + //End of entry + state = PRELHS; + break; + case '=': + lhs_end: + if (state == PRERHS) { + state = RHS; + skip_spaces(cfg, &line, &col); + break; + } + if (state != LHS) + goto plain_char; + *e_next = malloc(sizeof(struct entry)); + e_curr = *e_next; + memset(e_curr, 0, sizeof(*e_curr)); + e_next = &e_curr->next; + e_curr->lhs_len = buf.len; + e_curr->lhs = finalize_buf(&buf); + sec_curr->nent++; + rhs_next = &e_curr->r; + skip_spaces(cfg, &line, &col); + state = c == '=' ? RHS : PRERHS; + break; + default: + plain_char: + if (state == PRELHS) { + if (sec_head == NULL) + return NULL; + state = LHS; + } else if (state == PRERHS) + state = LHS; + if (c == EOF) + break; + push_char(c, &buf); + } + } while(c != EOF); +end: + fclose(cfg); + *_nsec = nsec; + return sec_head; +err: + fclose(cfg); + *_nsec = 0; + free(buf.buf); + free_sections(sec_head); + + fprintf(stderr, "Failed to parse config, line %d, col %d: Unexpected '%c'\n", line, col, c); + return NULL; +} diff --git a/src/brp/parser.h b/src/brp/parser.h new file mode 100644 index 0000000..e01284e --- /dev/null +++ b/src/brp/parser.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +struct rhs { + char *str; + size_t len; + struct rhs *next; +}; +struct entry { + char *lhs; + size_t lhs_len; + int nrhs; + struct rhs *r; + struct entry *next; +}; +struct section { + char *name; + size_t name_len; + int nent; + struct entry *e; + struct section *next; +}; + +static inline void free_sections(struct section *sec_head) { + struct entry *e_curr; + struct rhs *rhs_curr; + while(sec_head) { + free(sec_head->name); + e_curr = sec_head->e; + while(e_curr) { + free(e_curr->lhs); + rhs_curr = e_curr->r; + while(rhs_curr) { + struct rhs *tmp = rhs_curr->next; + free(rhs_curr->str); + free(rhs_curr); + rhs_curr = tmp; + } + + struct entry *tmp = e_curr->next; + free(e_curr); + e_curr = tmp; + } + + struct section *tmp = sec_head->next; + free(sec_head); + sec_head = tmp; + } +} +static inline void print_sections(struct section *sec_head) { + struct entry *e_curr; + struct rhs *rhs_curr; + while(sec_head) { + printf("[%s] (%d)\n", sec_head->name, sec_head->nent); + e_curr = sec_head->e; + while(e_curr) { + printf("%s (%d)", e_curr->lhs, e_curr->nrhs); + if (e_curr->r) + printf(" = "); + else + printf("\n"); + rhs_curr = e_curr->r; + while(rhs_curr) { + if (rhs_curr->next) + printf("%s, ", rhs_curr->str); + else + printf("%s\n", rhs_curr->str); + rhs_curr = rhs_curr->next; + } + e_curr = e_curr->next; + } + + sec_head = sec_head->next; + } +} + +struct section *parse_config(const char *, int *); diff --git a/src/slash-bedrock/sbin/brn b/src/slash-bedrock/sbin/brn index 9526454..d0e894d 100755 --- a/src/slash-bedrock/sbin/brn +++ b/src/slash-bedrock/sbin/brn @@ -371,6 +371,9 @@ enable_strata() { then rm /bedrock/run/deleteme fi + + # Make brp search the newly enabled stratum + echo 1 > /bedrock/brpath/reparse_config & } mount_fstab() {