diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h index 6b9089d..85b0cfc 100644 --- a/libselinux/include/selinux/selinux.h +++ b/libselinux/include/selinux/selinux.h @@ -496,7 +496,9 @@ extern const char *selinux_policy_root(void); /* These functions return the paths to specific files under the policy root directory. */ +extern const char *selinux_current_policy_path(void); extern const char *selinux_binary_policy_path(void); +extern char *selinux_binary_policy_path_min_max(int min, int *max); extern const char *selinux_failsafe_context_path(void); extern const char *selinux_removable_context_path(void); extern const char *selinux_default_context_path(void); diff --git a/libselinux/man/man3/selinux_binary_policy_path.3 b/libselinux/man/man3/selinux_binary_policy_path.3 index 8ead1a4..c68ace5 100644 --- a/libselinux/man/man3/selinux_binary_policy_path.3 +++ b/libselinux/man/man3/selinux_binary_policy_path.3 @@ -17,6 +17,8 @@ extern const char *selinux_policy_root(void); extern const char *selinux_binary_policy_path(void); +extern const char *selinux_current_policy_path(void); + extern const char *selinux_failsafe_context_path(void); extern const char *selinux_removable_context_path(void); @@ -52,7 +54,9 @@ selinux_path() - top-level SELinux configuration directory .sp selinux_policy_root() - top-level policy directory .sp -selinux_binary_policy_path() - binary policy file loaded into kernel +selinux_current_policy_path() - binary policy file loaded into kernel +.sp +selinux_binary_policy_path() - binary policy path on disk .sp selinux_default_type_path - context file mapping roles to default types. .sp diff --git a/libselinux/src/audit2why.c b/libselinux/src/audit2why.c index 02483a3..89953d7 100644 --- a/libselinux/src/audit2why.c +++ b/libselinux/src/audit2why.c @@ -206,27 +206,12 @@ static int __policy_init(const char *init_path) return 1; } } else { - vers = sepol_policy_kern_vers_max(); - if (vers < 0) { - snprintf(errormsg, sizeof(errormsg), - "Could not get policy version: %s\n", - strerror(errno)); - PyErr_SetString( PyExc_ValueError, errormsg); - return 1; - } - snprintf(path, PATH_MAX, "%s.%d", - selinux_binary_policy_path(), vers); - fp = fopen(path, "r"); - while (!fp && errno == ENOENT && --vers) { - snprintf(path, PATH_MAX, "%s.%d", - selinux_binary_policy_path(), vers); - fp = fopen(path, "r"); - } + fp = fopen(selinux_current_policy_path(), "r"); if (!fp) { snprintf(errormsg, sizeof(errormsg), - "unable to open %s.%d: %s\n", - selinux_binary_policy_path(), - security_policyvers(), strerror(errno)); + "unable to open %s: %s\n", + selinux_current_policy_path(), + strerror(errno)); PyErr_SetString( PyExc_ValueError, errormsg); return 1; } diff --git a/libselinux/src/avc.c b/libselinux/src/avc.c index 802a07f..6ff83a7 100644 --- a/libselinux/src/avc.c +++ b/libselinux/src/avc.c @@ -827,6 +827,7 @@ int avc_has_perm(security_id_t ssid, security_id_t tsid, errsave = errno; avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); errno = errsave; + if (!avc_enforcing) return 0; return rc; } diff --git a/libselinux/src/file_path_suffixes.h b/libselinux/src/file_path_suffixes.h index 825f295..d11c8dc 100644 --- a/libselinux/src/file_path_suffixes.h +++ b/libselinux/src/file_path_suffixes.h @@ -26,4 +26,4 @@ S_(BINPOLICY, "/policy/policy") S_(FILE_CONTEXT_SUBS, "/contexts/files/file_contexts.subs") S_(FILE_CONTEXT_SUBS_DIST, "/contexts/files/file_contexts.subs_dist") S_(SEPGSQL_CONTEXTS, "/contexts/sepgsql_contexts") - S_(BOOLEAN_SUBS, "/booleans.subs") + S_(BOOLEAN_SUBS, "/booleans.subs_dist") diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c index 02b3cd2..fad8bbd 100644 --- a/libselinux/src/label_file.c +++ b/libselinux/src/label_file.c @@ -8,6 +8,7 @@ * developed by Secure Computing Corporation. */ +#include #include #include #include @@ -16,7 +17,12 @@ #include #include #include +#include #include + +#include + +#include #include #include #include @@ -229,6 +235,167 @@ static int process_line(struct selabel_handle *rec, return 0; } +static int load_mmap(struct selabel_handle *rec, const char *path, struct stat *stat) +{ + struct saved_data *data = (struct saved_data *)rec->data; + char mmap_path[PATH_MAX + 1]; + int mmapfd; + int rc, i; + struct stat mmap_stat; + char *addr; + size_t len; + int stem_map_len, *stem_map; + + uint32_t *magic; + uint32_t *section_len; + uint32_t *plen; + + rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path); + if (rc >= sizeof(mmap_path)) + return -1; + + mmapfd = open(mmap_path, O_RDONLY); + if (!mmapfd) + return -1; + + rc = fstat(mmapfd, &mmap_stat); + if (rc < 0) + return -1; + + /* if mmap is old, ignore it */ + if (mmap_stat.st_mtime < stat->st_mtime) + return -1; + + if (mmap_stat.st_mtime == stat->st_mtime && + mmap_stat.st_mtim.tv_nsec < stat->st_mtim.tv_nsec) + return -1; + + /* ok, read it in... */ + len = mmap_stat.st_size; + len += (sysconf(_SC_PAGE_SIZE) - 1); + len &= ~(sysconf(_SC_PAGE_SIZE) - 1); + + addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0); + close(mmapfd); + if (addr == MAP_FAILED) { + perror("mmap"); + return -1; + } + + /* check if this looks like an fcontext file */ + magic = (uint32_t *)addr; + if (*magic != SELINUX_MAGIC_COMPILED_FCONTEXT) + return -1; + addr += sizeof(uint32_t); + + /* check if this version is higher than we understand */ + section_len = (uint32_t *)addr; + if (*section_len > SELINUX_COMPILED_FCONTEXT_MAX_VERS) + return -1; + addr += sizeof(uint32_t); + + /* allocate the stems_data array */ + section_len = (uint32_t *)addr; + addr += sizeof(uint32_t); + + /* + * map indexed by the stem # in the mmap file and contains the stem + * number in the data stem_arr + */ + stem_map_len = *section_len; + stem_map = calloc(stem_map_len, sizeof(*stem_map)); + if (!stem_map) + return -1; + + for (i = 0; i < *section_len; i++) { + char *buf; + uint32_t stem_len; + int newid; + + /* the length does not inlude the nul */ + plen = (uint32_t *)addr; + addr += sizeof(uint32_t); + + stem_len = *plen; + buf = (char *)addr; + addr += (stem_len + 1); // +1 is the nul + + /* store the mapping between old and new */ + newid = find_stem(data, buf, stem_len); + if (newid < 0) { + newid = store_stem(data, buf, stem_len); + if (newid < 0) + return newid; + data->stem_arr[newid].from_mmap = 1; + } + stem_map[i] = newid; + } + + /* allocate the regex array */ + section_len = (uint32_t *)addr; + addr += sizeof(*section_len); + + for (i = 0; i < *section_len; i++) { + struct spec *spec; + int32_t stem_id; + + rc = grow_specs(data); + if (rc < 0) + return rc; + + spec = &data->spec_arr[data->nspec]; + spec->from_mmap = 1; + spec->regcomp = 1; + + plen = (uint32_t *)addr; + addr += sizeof(uint32_t); + spec->lr.ctx_raw = strdup((char *)addr); + if (!spec->lr.ctx_raw) + return -1; + addr += *plen; + + plen = (uint32_t *)addr; + addr += sizeof(uint32_t); + spec->regex_str = (char *)addr; + addr += *plen; + + spec->mode = *(mode_t *)addr; + addr += sizeof(mode_t); + + /* map the stem id from the mmap file to the data->stem_arr */ + stem_id = *(int32_t *)addr; + if (stem_id == -1) { + spec->stem_id = -1; + } else { + assert(stem_id <= stem_map_len); + spec->stem_id = stem_map[stem_id]; + } + addr += sizeof(int32_t); + + /* retrieve the hasMetaChars bit */ + spec->hasMetaChars = *(uint32_t *)addr; + addr += sizeof(uint32_t); + + plen = (uint32_t *)addr; + addr += sizeof(uint32_t); + spec->regex = (pcre *)addr; + addr += *plen; + + plen = (uint32_t *)addr; + addr += sizeof(uint32_t); + spec->lsd.study_data = (void *)addr; + spec->lsd.flags |= PCRE_EXTRA_STUDY_DATA; + addr += *plen; + + data->nspec++; + } + + free(stem_map); + + /* win */ + return 0; +} + static int process_file(const char *path, const char *suffix, struct selabel_handle *rec, const char *prefix) { FILE *fp; @@ -261,6 +428,10 @@ static int process_file(const char *path, const char *suffix, struct selabel_han return -1; } + rc = load_mmap(rec, path, &sb); + if (rc == 0) + goto out; + /* * The do detailed validation of the input and fill the spec array */ @@ -270,6 +441,7 @@ static int process_file(const char *path, const char *suffix, struct selabel_han if (rc) return rc; } +out: free(line_buf); fclose(fp); @@ -357,6 +529,8 @@ static void closef(struct selabel_handle *rec) for (i = 0; i < data->nspec; i++) { spec = &data->spec_arr[i]; + if (spec->from_mmap) + continue; free(spec->regex_str); free(spec->type_str); free(spec->lr.ctx_raw); @@ -369,6 +543,8 @@ static void closef(struct selabel_handle *rec) for (i = 0; i < (unsigned int)data->num_stems; i++) { stem = &data->stem_arr[i]; + if (stem->from_mmap) + continue; free(stem->buf); } diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h index cb5633b..9799bbb 100644 --- a/libselinux/src/label_file.h +++ b/libselinux/src/label_file.h @@ -5,24 +5,32 @@ #include "label_internal.h" +#define SELINUX_MAGIC_COMPILED_FCONTEXT 0xf97cff8a +#define SELINUX_COMPILED_FCONTEXT_MAX_VERS 1 + /* A file security context specification. */ struct spec { struct selabel_lookup_rec lr; /* holds contexts for lookup result */ char *regex_str; /* regular expession string for diagnostics */ char *type_str; /* type string for diagnostic messages */ pcre *regex; /* compiled regular expression */ - pcre_extra *sd; /* extra compiled stuff */ + union { + pcre_extra *sd; /* pointer to extra compiled stuff */ + pcre_extra lsd; /* used to hold the mmap'd version */ + }; mode_t mode; /* mode format value */ int matches; /* number of matching pathnames */ int stem_id; /* indicates which stem-compression item */ char hasMetaChars; /* regular expression has meta-chars */ char regcomp; /* regex_str has been compiled to regex */ + char from_mmap; /* this spec is from an mmap of the data */ }; /* A regular expression stem */ struct stem { char *buf; int len; + char from_mmap; }; /* Our stored configuration */ @@ -45,7 +53,10 @@ struct saved_data { static inline pcre_extra *get_pcre_extra(struct spec *spec) { - return spec->sd; + if (spec->from_mmap) + return &spec->lsd; + else + return spec->sd; } static inline mode_t string_to_mode(char *mode) diff --git a/libselinux/src/load_policy.c b/libselinux/src/load_policy.c index 10e29b9..888dab5 100644 --- a/libselinux/src/load_policy.c +++ b/libselinux/src/load_policy.c @@ -49,8 +49,9 @@ int load_setlocaldefs hidden = 1; int selinux_mkload_policy(int preservebools) { int kernvers = security_policyvers(); - int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION, vers; + int maxvers = kernvers, minvers = DEFAULT_POLICY_VERSION; int setlocaldefs = load_setlocaldefs; + char *pol_path = NULL; char path[PATH_MAX]; struct stat sb; struct utsname uts; @@ -162,29 +163,24 @@ checkbool: maxvers = max(kernvers, maxvers); } - vers = maxvers; - search: - snprintf(path, sizeof(path), "%s.%d", - selinux_binary_policy_path(), vers); - fd = open(path, O_RDONLY); - while (fd < 0 && errno == ENOENT - && --vers >= minvers) { - /* Check prior versions to see if old policy is available */ - snprintf(path, sizeof(path), "%s.%d", - selinux_binary_policy_path(), vers); - fd = open(path, O_RDONLY); +search: + pol_path = selinux_binary_policy_path_min_max(minvers, &maxvers); + if (!pol_path) { + fprintf(stderr, "SELinux: unable to find usable policy file: %s\n", + strerror(errno)); + goto dlclose; } + + fd = open(pol_path, O_RDONLY); if (fd < 0) { - fprintf(stderr, - "SELinux: Could not open policy file <= %s.%d: %s\n", - selinux_binary_policy_path(), maxvers, strerror(errno)); + fprintf(stderr, "SELinux: Could not open policy file %s: %s\n", + pol_path, strerror(errno)); goto dlclose; } if (fstat(fd, &sb) < 0) { - fprintf(stderr, - "SELinux: Could not stat policy file %s: %s\n", - path, strerror(errno)); + fprintf(stderr, "SELinux: Could not stat policy file %s: %s\n", + pol_path, strerror(errno)); goto close; } @@ -195,13 +191,12 @@ checkbool: size = sb.st_size; data = map = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) { - fprintf(stderr, - "SELinux: Could not map policy file %s: %s\n", - path, strerror(errno)); + fprintf(stderr, "SELinux: Could not map policy file %s: %s\n", + pol_path, strerror(errno)); goto close; } - if (vers > kernvers && usesepol) { + if (maxvers > kernvers && usesepol) { /* Need to downgrade to kernel-supported version. */ if (policy_file_create(&pf)) goto unmap; @@ -220,12 +215,12 @@ checkbool: /* Downgrade failed, keep searching. */ fprintf(stderr, "SELinux: Could not downgrade policy file %s, searching for an older version.\n", - path); + pol_path); policy_file_free(pf); policydb_free(policydb); munmap(map, sb.st_size); close(fd); - vers--; + maxvers--; goto search; } policy_file_free(pf); @@ -281,7 +276,7 @@ checkbool: if (rc) fprintf(stderr, "SELinux: Could not load policy file %s: %s\n", - path, strerror(errno)); + pol_path, strerror(errno)); unmap: if (data != map) @@ -296,6 +291,7 @@ checkbool: if (libsepolh) dlclose(libsepolh); #endif + free(pol_path); return rc; } diff --git a/libselinux/src/matchpathcon.c b/libselinux/src/matchpathcon.c index 2d7369e..2a00807 100644 --- a/libselinux/src/matchpathcon.c +++ b/libselinux/src/matchpathcon.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "selinux_internal.h" #include "label_internal.h" #include "callbacks.h" @@ -62,7 +63,7 @@ static void { va_list ap; va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + vsyslog(LOG_ERR, fmt, ap); va_end(ap); } diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c index 296f357..cb65666 100644 --- a/libselinux/src/selinux_config.c +++ b/libselinux/src/selinux_config.c @@ -9,6 +9,7 @@ #include #include #include "selinux_internal.h" +#include "policy.h" #include "get_default_type_internal.h" #define SELINUXDIR "/etc/selinux/" @@ -296,13 +297,57 @@ const char *selinux_removable_context_path(void) hidden_def(selinux_removable_context_path) +char *selinux_binary_policy_path_min_max(int min, int *max) +{ + int ret; + char *path = NULL; + + while(*max >= min) { + ret = asprintf(&path, "%s.%d", get_path(BINPOLICY), *max); + if (ret < 0) + goto err; + ret = access(path, R_OK); + if (!ret) + return path; + free(path); + path = NULL; + *max = *max - 1; + } +err: + free(path); + return NULL; +} +hidden_def(selinux_binary_policy_path_min_max) + const char *selinux_binary_policy_path(void) { return get_path(BINPOLICY); } - hidden_def(selinux_binary_policy_path) +const char *selinux_current_policy_path(void) +{ + int rc = 0; + int vers = 0; + static char policy_path[PATH_MAX]; + + snprintf(policy_path, sizeof(policy_path), "%s/policy", selinux_mnt); + if (access(policy_path, F_OK) != 0 ) { + vers = security_policyvers(); + do { + /* Check prior versions to see if old policy is available */ + snprintf(policy_path, sizeof(policy_path), "%s.%d", + selinux_binary_policy_path(), vers); + } while ((rc = access(policy_path, F_OK)) && --vers > 0); + + if (rc) return NULL; + } + + return policy_path; +} + +hidden_def(selinux_current_policy_path) + const char *selinux_file_context_path(void) { return get_path(FILE_CONTEXTS); diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h index 2c7c85c..75ebc88 100644 --- a/libselinux/src/selinux_internal.h +++ b/libselinux/src/selinux_internal.h @@ -61,7 +61,9 @@ hidden_proto(selinux_mkload_policy) hidden_proto(security_deny_unknown) hidden_proto(selinux_boolean_sub) hidden_proto(selinux_binary_policy_path) + hidden_proto(selinux_binary_policy_path_min_max); hidden_proto(selinux_booleans_subs_path) + hidden_proto(selinux_current_policy_path) hidden_proto(selinux_default_context_path) hidden_proto(selinux_securetty_types_path) hidden_proto(selinux_failsafe_context_path) diff --git a/libselinux/src/seusers.c b/libselinux/src/seusers.c index cfea186..8b1eba6 100644 --- a/libselinux/src/seusers.c +++ b/libselinux/src/seusers.c @@ -125,7 +125,7 @@ static int check_group(const char *group, const char *name, const gid_t gid) { rbuf = malloc(rbuflen); if (rbuf == NULL) return 0; - int retval = getgrnam_r(group, &gbuf, rbuf, + int retval = getgrnam_r(group, &gbuf, rbuf, rbuflen, &grent); if ( retval == ERANGE ) { @@ -198,13 +198,13 @@ int getseuserbyname(const char *name, char **r_seuser, char **r_level) if (!strcmp(username, name)) break; - if (username[0] == '%' && - !groupseuser && + if (username[0] == '%' && + !groupseuser && check_group(&username[1], name, gid)) { groupseuser = seuser; grouplevel = level; } else { - if (!defaultseuser && + if (!defaultseuser && !strcmp(username, "__default__")) { defaultseuser = seuser; defaultlevel = level; @@ -258,7 +258,7 @@ int getseuserbyname(const char *name, char **r_seuser, char **r_level) return 0; } -int getseuser(const char *username, const char *service, +int getseuser(const char *username, const char *service, char **r_seuser, char **r_level) { int ret = -1; int len = 0; diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore index 8b9294d..060eaab 100644 --- a/libselinux/utils/.gitignore +++ b/libselinux/utils/.gitignore @@ -13,6 +13,7 @@ getsebool getseuser matchpathcon policyvers +sefcontext_compile selinux_check_securetty_context selinuxenabled selinuxexeccon diff --git a/libselinux/utils/Makefile b/libselinux/utils/Makefile index 5f3e047..f469924 100644 --- a/libselinux/utils/Makefile +++ b/libselinux/utils/Makefile @@ -28,6 +28,7 @@ LDLIBS += -L../src -lselinux -L$(LIBDIR) TARGETS=$(patsubst %.c,%,$(wildcard *.c)) +sefcontext_compile: LDLIBS += -lpcre ifeq ($(DISABLE_AVC),y) UNUSED_TARGETS+=compute_av compute_create compute_member compute_relabel diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c new file mode 100644 index 0000000..f8a5fea --- /dev/null +++ b/libselinux/utils/sefcontext_compile.c @@ -0,0 +1,345 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "../src/label_file.h" + +static int process_file(struct saved_data *data, const char *filename) +{ + struct spec *spec; + unsigned int line_num; + char *line_buf = NULL; + size_t line_len; + ssize_t len; + FILE *context_file; + + context_file = fopen(filename, "r"); + if (!context_file) + return -1; + + line_num = 0; + while ((len = getline(&line_buf, &line_len, context_file)) != -1) { + char *context; + char *mode; + char *regex; + char *cp, *anchored_regex; + char *buf_p; + pcre *re; + pcre_extra *sd; + const char *err; + int items, erroff, rc; + size_t regex_len; + int32_t stem_id; + + len = strlen(line_buf); + if (line_buf[len - 1] == '\n') + line_buf[len - 1] = 0; + buf_p = line_buf; + while (isspace(*buf_p)) + buf_p++; + /* Skip comment lines and empty lines. */ + if (*buf_p == '#' || *buf_p == 0) + continue; + + items = sscanf(line_buf, "%ms %ms %ms", ®ex, &mode, &context); + if (items < 2 || items > 3) { + fprintf(stderr, "invalid entry, skipping:%s", line_buf); + continue; + } + + if (items == 2) { + context = mode; + mode = NULL; + } + + rc = grow_specs(data); + if (rc) + return rc; + + spec = &data->spec_arr[data->nspec]; + + spec->lr.ctx_raw = context; + spec->mode = string_to_mode(mode); + if (spec->mode == -1) { + fprintf(stderr, "%s: line %d has invalid file type %s\n", + regex, line_num + 1, mode); + spec->mode = 0; + } + free(mode); + spec->regex_str = regex; + + stem_id = find_stem_from_spec(data, regex); + spec->stem_id = stem_id; + /* skip past the fixed stem part */ + if (stem_id != -1) + regex += data->stem_arr[stem_id].len; + + regex_len = strlen(regex); + cp = anchored_regex = malloc(regex_len + 3); + if (!cp) + return -1; + + *cp++ = '^'; + memcpy(cp, regex, regex_len); + cp += regex_len; + *cp++ = '$'; + *cp = '\0'; + + spec_hasMetaChars(spec); + + re = pcre_compile(anchored_regex, 0, &err, &erroff, NULL); + if (!re) { + fprintf(stderr, "PCRE compilation failed for %s at offset %d: %s\n", anchored_regex, erroff, err); + return -1; + } + spec->regex = re; + + sd = pcre_study(re, 0, &err); + if (!sd) { + fprintf(stderr, "PCRE study failed for %s: %s\n", anchored_regex, err); + return -1; + } + free(anchored_regex); + spec->sd = sd; + + line_num++; + data->nspec++; + } + + free(line_buf); + fclose(context_file); + + return 0; +} + +/* + * File Format + * + * u32 - magic number + * u32 - version + * u32 - number of stems + * ** Stems + * u32 - length of stem EXCLUDING nul + * char - stem char array INCLUDING nul + * u32 - number of regexs + * ** Regexes + * u32 - length of upcoming context INCLUDING nul + * char - char array of the raw context + * u32 - length of the upcoming regex_str + * char - char array of the original regex string including the stem. + * mode_t - mode bits + * s32 - stemid associated with the regex + * u32 - spec has meta characters + * u32 - data length of the pcre regex + * char - a bufer holding the raw pcre regex info + * u32 - data length of the pcre regex study daya + * char - a buffer holding the raw pcre regex study data + */ +static int write_binary_file(struct saved_data *data, char *filename) +{ + struct spec *specs = data->spec_arr; + FILE *bin_file; + size_t len; + uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT; + uint32_t section_len; + uint32_t i; + + bin_file = fopen(filename, "w"); + if (!bin_file) { + perror("fopen output_file"); + exit(EXIT_FAILURE); + } + + /* write some magic number */ + len = fwrite(&magic, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* write the version */ + section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS; + len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* write the number of stems coming */ + section_len = data->num_stems; + len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + for (i = 0; i < section_len; i++) { + char *stem = data->stem_arr[i].buf; + uint32_t stem_len = data->stem_arr[i].len; + + /* write the strlen (aka no nul) */ + len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* include the nul in the file */ + stem_len += 1; + len = fwrite(stem, sizeof(char), stem_len, bin_file); + if (len != stem_len) + return -1; + } + + /* write the number of regexes coming */ + section_len = data->nspec; + len = fwrite(§ion_len, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + for (i = 0; i < section_len; i++) { + char *context = specs[i].lr.ctx_raw; + char *regex_str = specs[i].regex_str; + mode_t mode = specs[i].mode; + int32_t stem_id = specs[i].stem_id; + pcre *re = specs[i].regex; + pcre_extra *sd = get_pcre_extra(&specs[i]); + uint32_t to_write; + size_t size; + int rc; + + /* length of the context string (including nul) */ + to_write = strlen(context) + 1; + len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* original context strin (including nul) */ + len = fwrite(context, sizeof(char), to_write, bin_file); + if (len != to_write) + return -1; + + /* length of the original regex string (including nul) */ + to_write = strlen(regex_str) + 1; + len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* original regex string */ + len = fwrite(regex_str, sizeof(char), to_write, bin_file); + if (len != to_write) + return -1; + + /* binary F_MODE bits */ + len = fwrite(&mode, sizeof(mode), 1, bin_file); + if (len != 1) + return -1; + + /* stem for this regex (could be -1) */ + len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file); + if (len != 1) + return -1; + + /* does this spec have a metaChar? */ + to_write = specs[i].hasMetaChars; + len = fwrite(&to_write, sizeof(to_write), 1, bin_file); + if (len != 1) + return -1; + + /* determine the size of the pcre data in bytes */ + rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size); + if (rc < 0) + return -1; + + /* write the number of bytes in the pcre data */ + to_write = size; + len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* write the actual pcre data as a char array */ + len = fwrite(re, 1, to_write, bin_file); + if (len != to_write) + return -1; + + /* determine the size of the pcre study info */ + rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size); + if (rc < 0) + return -1; + + /* write the number of bytes in the pcre study data */ + to_write = size; + len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file); + if (len != 1) + return -1; + + /* write the actual pcre study data as a char array */ + len = fwrite(sd->study_data, 1, to_write, bin_file); + if (len != to_write) + return -1; + } + + fclose(bin_file); + + return 0; +} + +static int free_specs(struct saved_data *data) +{ + struct spec *specs = data->spec_arr; + unsigned int num_entries = data->nspec; + unsigned int i; + + for (i = 0; i < num_entries; i++) { + free(specs[i].lr.ctx_raw); + free(specs[i].lr.ctx_trans); + free(specs[i].regex_str); + pcre_free(specs[i].regex); + pcre_free_study(specs[i].sd); + } + free(specs); + + num_entries = data->num_stems; + for (i = 0; i < num_entries; i++) { + free(data->stem_arr[i].buf); + } + free(data->stem_arr); + + memset(data, 0, sizeof(*data)); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct saved_data data; + const char *path; + char stack_path[PATH_MAX + 1]; + int rc; + + if (argc != 2) { + fprintf(stderr, "usage: %s input_file\n", argv[0]); + exit(EXIT_FAILURE); + } + + memset(&data, 0, sizeof(data)); + + path = argv[1]; + + rc = process_file(&data, path); + if (rc < 0) + return rc; + + rc = sort_specs(&data); + if (rc) + return rc; + + rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path); + if (rc < 0 || rc >= sizeof(stack_path)) + return rc; + rc = write_binary_file(&data, stack_path); + if (rc < 0) + return rc; + + rc = free_specs(&data); + if (rc < 0) + return rc; + + return 0; +}