From: Eric Biggers Date: Wed, 27 Mar 2013 03:40:53 +0000 (-0500) Subject: Move capture config parsing to imagex X-Git-Tag: v1.3.2~5 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=7e7aeb2b91b492886ebdc28e5bac0b25462c154b Move capture config parsing to imagex --- diff --git a/Makefile.am b/Makefile.am index 663691fb..b3bb6917 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ AM_CFLAGS = -std=gnu99 lib_LTLIBRARIES = libwim.la -libwim_la_LDFLAGS = -version-info 5:0:3 $(WINDOWS_LDFLAGS) +libwim_la_LDFLAGS = -version-info 5:0:0 $(WINDOWS_LDFLAGS) libwim_la_SOURCES = \ src/add_image.c \ diff --git a/README b/README index b1c6540d..f17ff6f5 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ WIMLIB -This is wimlib version 1.3.2 (March 2013). wimlib can be used to read, -write, and mount files in the Windows Imaging Format (WIM files). These files -are normally created by using the `imagex.exe' utility on Windows, but this -library provides a free implementation of imagex for UNIX-based systems. +This is wimlib version 1.3.2 (March 2013). wimlib can be used to read, write, +and mount files in the Windows Imaging Format (WIM files). These files are +normally created by using the `imagex.exe' utility on Windows, but this library +provides a free implementation of ImageX for UNIX-based systems. wimlib 1.3.0 has added experimental support for Windows. See the file "README.WINDOWS" for more details. diff --git a/doc/imagex-capture.1.in b/doc/imagex-capture.1.in index 76b10e73..c38758a0 100644 --- a/doc/imagex-capture.1.in +++ b/doc/imagex-capture.1.in @@ -180,30 +180,32 @@ capture mode. Specifies a configuration file for capturing the new image. The configuration file specifies files that are to be treated specially during the image capture. -The format of the configuration file is a number of sections containing file +The format of the configuration file is a number of sections containing path globs one per line, where each section begins with the tag [ExclusionList], [ExclusionException], [CompressionExclusionList], or [AlignmentList]. Currently, only the [ExclusionList] and [ExclusionException] sections are -implemented. The [ExclusionList] section specifies a list of file globs to +implemented. The [ExclusionList] section specifies a list of path globs to exclude from capture, while the [ExclusionException] section specifies a list of -file globs to include in the capture even if the matched file or directory name +path globs to include in the capture even if the matched file or directory name also appears in the [ExclusionList]. -Relative globs with only one path component match against a filename in any -directory. Relative globs with multiple path components, as well as absolute -globs, are treated as paths starting at the root directory of capture, or the -root of the NTFS volume for NTFS capture mode. If a directory is matched by a -glob in the [ExclusionList], the entire directory tree rooted at that directory -is excluded from the capture, unless \fB--dereference\fR is specified and there -is another path into that directory through a symbolic link. +Relative globs with only one path component (e.g. *.mp3) match against a filename in any +directory. Relative globs with multiple path components (e.g. dir/file), +as well as absolute globs (e.g. /dir/file), are treated as paths starting at the +root directory of capture, or the root of the NTFS volume for NTFS capture mode. +If a directory is matched by a glob in the [ExclusionList], the entire directory +tree rooted at that directory is excluded from the capture, unless +\fB--dereference\fR is specified and there is another path into that directory +through a symbolic link. For compatibility with Windows, the path separators in the globs may be either forward slashes or backslashes, and the line separators may be either UNIX-style -or DOS-style. However, globs with spaces in them currently must not be quoted. -Empty lines are ignored. +or DOS-style. Globs with spaces in them must be quoted, and leading and +trailing whitespace is not significant. Empty lines and lines beginning with +'#' or whitespace followed by '#' are ignored. -On UNIX, drive letters are stripped from all patterns. On Windows, drive -letters are allowed, but must specify absolute paths and are treated as such. +Paths may not have drive letters in them, as they are all relative to the root +of capture and not absolute external paths. If this option is not specified the following default configuration file is used: @@ -216,7 +218,7 @@ used: \\$ntfs.log \\hiberfil.sys \\pagefile.sys -\\System Volume Information +"\\System Volume Information" \\RECYCLER \\Windows\\CSC .RE diff --git a/programs/imagex.c b/programs/imagex.c index 17cc36f6..ffc841f6 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -369,16 +369,28 @@ file_get_size(const tchar *filename) return (off_t)-1; } -static const tchar *default_capture_config = -T( -"[ExclusionList]\n" -"\\$ntfs.log\n" -"\\hiberfil.sys\n" -"\\pagefile.sys\n" -"\\System Volume Information\n" -"\\RECYCLER\n" -"\\Windows\\CSC\n" -); +tchar pat_ntfs_log[] = T("/$ntfs.log"); +tchar pat_hiberfil_sys[] = T("/hiberfil.sys"); +tchar pat_pagefile_sys[] = T("/pagefile.sys"); +tchar pat_system_volume_information[] = T("/System Volume Information"); +tchar pat_recycler[] = T("/RECYCLER"); +tchar pat_windows_csc[] = T("/Windows/CSC"); + +tchar *default_pats[] = { + pat_ntfs_log, + pat_hiberfil_sys, + pat_pagefile_sys, + pat_system_volume_information, + pat_recycler, + pat_windows_csc, +}; + +static struct wimlib_capture_config default_capture_config = { + .exclusion_pats = { + .num_pats = sizeof(default_pats) / sizeof(*default_pats), + .pats = default_pats, + }, +}; enum { PARSE_FILENAME_SUCCESS = 0, @@ -499,6 +511,34 @@ is_comment_line(const tchar *line, size_t len) } } +static ssize_t +text_file_count_lines(tchar **contents_p, size_t *nchars_p) +{ + ssize_t nlines = 0; + tchar *contents = *contents_p; + size_t nchars = *nchars_p; + size_t i; + + for (i = 0; i < nchars; i++) + if (contents[i] == T('\n')) + nlines++; + + /* Handle last line not terminated by a newline */ + if (nchars != 0 && contents[nchars - 1] != T('\n')) { + contents = realloc(contents, (nchars + 1) * sizeof(tchar)); + if (!contents) { + imagex_error(T("Out of memory!")); + return -1; + } + contents[nchars] = T('\n'); + *contents_p = contents; + nchars++; + nlines++; + } + *nchars_p = nchars; + return nlines; +} + /* Parses a file in the source list format. (See the man page for * 'wimlib-imagex capture' for details on this format and the meaning.) * @@ -520,37 +560,24 @@ static struct wimlib_capture_source * parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars, size_t *nsources_ret) { - size_t nlines; + ssize_t nlines; tchar *p; struct wimlib_capture_source *sources; size_t i, j; - tchar *source_list_contents = *source_list_contents_p; - nlines = 0; - for (i = 0; i < source_list_nchars; i++) - if (source_list_contents[i] == T('\n')) - nlines++; - - /* Handle last line not terminated by a newline */ - if (source_list_nchars != 0 && - source_list_contents[source_list_nchars - 1] != T('\n')) - { - source_list_contents = realloc(source_list_contents, - (source_list_nchars + 1) * sizeof(tchar)); - if (!source_list_contents) - goto oom; - source_list_contents[source_list_nchars] = T('\n'); - *source_list_contents_p = source_list_contents; - source_list_nchars++; - nlines++; - } + nlines = text_file_count_lines(source_list_contents_p, + &source_list_nchars); + if (nlines < 0) + return NULL; /* Always allocate at least 1 slot, just in case the implementation of * calloc() returns NULL if 0 bytes are requested. */ sources = calloc(nlines ?: 1, sizeof(*sources)); - if (!sources) - goto oom; - p = source_list_contents; + if (!sources) { + imagex_error(T("out of memory")); + return NULL; + } + p = *source_list_contents_p; j = 0; for (i = 0; i < nlines; i++) { /* XXX: Could use rawmemchr() here instead, but it may not be @@ -569,9 +596,145 @@ parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars, } *nsources_ret = j; return sources; -oom: - imagex_error(T("out of memory")); - return NULL; +} + + +enum capture_config_section { + CAPTURE_CONFIG_NO_SECTION, + CAPTURE_CONFIG_EXCLUSION_SECTION, + CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION, + CAPTURE_CONFIG_IGNORE_SECTION, +}; + +enum { + CAPTURE_CONFIG_INVALID_SECTION, + CAPTURE_CONFIG_CHANGED_SECTION, + CAPTURE_CONFIG_SAME_SECTION, +}; + +static int +check_config_section(tchar *line, size_t len, + enum capture_config_section *cur_section) +{ + while (istspace(*line)) + line++; + + if (*line != T('[')) + return CAPTURE_CONFIG_SAME_SECTION; + + line++; + tchar *endbrace = tstrrchr(line, T(']')); + if (!endbrace) + return CAPTURE_CONFIG_SAME_SECTION; + + if (!tmemcmp(line, T("ExclusionList"), endbrace - line)) { + *cur_section = CAPTURE_CONFIG_EXCLUSION_SECTION; + } else if (!tmemcmp(line, T("ExclusionException"), endbrace - line)) { + *cur_section = CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION; + } else if (!tmemcmp(line, T("CompressionExclusionList"), endbrace - line)) { + *cur_section = CAPTURE_CONFIG_IGNORE_SECTION; + tfputs(T("WARNING: Ignoring [CompressionExclusionList] section " + "of capture config file\n"), + stderr); + } else if (!tmemcmp(line, T("AlignmentList"), endbrace - line)) { + *cur_section = CAPTURE_CONFIG_IGNORE_SECTION; + tfputs(T("WARNING: Ignoring [AlignmentList] section " + "of capture config file\n"), + stderr); + } else { + imagex_error(T("Invalid capture config file section \"%"TS"\""), + line - 1); + return CAPTURE_CONFIG_INVALID_SECTION; + } + return CAPTURE_CONFIG_CHANGED_SECTION; +} + + +static bool +pattern_list_add_pattern(struct wimlib_pattern_list *pat_list, + tchar *pat) +{ + if (pat_list->num_pats == pat_list->num_allocated_pats) { + tchar **pats; + size_t num_allocated_pats = pat_list->num_pats + 8; + + pats = realloc(pat_list->pats, + num_allocated_pats * sizeof(pat_list->pats[0])); + if (!pats) { + imagex_error(T("Out of memory!")); + return false; + } + pat_list->pats = pats; + pat_list->num_allocated_pats = num_allocated_pats; + } + pat_list->pats[pat_list->num_pats++] = pat; + return true; +} + +static bool +parse_capture_config_line(tchar *line, size_t len, + enum capture_config_section *cur_section, + struct wimlib_capture_config *config) +{ + tchar *filename; + int ret; + + ret = check_config_section(line, len, cur_section); + if (ret == CAPTURE_CONFIG_INVALID_SECTION) + return false; + if (ret == CAPTURE_CONFIG_CHANGED_SECTION) + return true; + + switch (*cur_section) { + case CAPTURE_CONFIG_NO_SECTION: + imagex_error(T("Line \"%"TS"\" is not in a section " + "(such as [ExclusionList]"), line); + return false; + case CAPTURE_CONFIG_EXCLUSION_SECTION: + if (parse_filename(&line, &len, &filename) != PARSE_FILENAME_SUCCESS) + return false; + return pattern_list_add_pattern(&config->exclusion_pats, + filename); + case CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION: + if (parse_filename(&line, &len, &filename) != PARSE_FILENAME_SUCCESS) + return false; + return pattern_list_add_pattern(&config->exclusion_exception_pats, + filename); + case CAPTURE_CONFIG_IGNORE_SECTION: + return true; + } + return false; +} + +static int +parse_capture_config(tchar **contents_p, size_t nchars, + struct wimlib_capture_config *config) +{ + ssize_t nlines; + tchar *p; + struct wimlib_capture_source *sources; + size_t i, j; + enum capture_config_section cur_section; + + memset(config, 0, sizeof(*config)); + + nlines = text_file_count_lines(contents_p, &nchars); + if (nlines < 0) + return -1; + + cur_section = CAPTURE_CONFIG_NO_SECTION; + p = *contents_p; + for (i = 0; i < nlines; i++) { + tchar *endp = tmemchr(p, T('\n'), nchars); + size_t len = endp - p + 1; + *endp = T('\0'); + if (!is_comment_line(p, len)) + if (!parse_capture_config_line(p, len, &cur_section, config)) + return -1; + p = endp + 1; + + } + return 0; } /* Reads the contents of a file into memory. */ @@ -1113,7 +1276,7 @@ imagex_capture_or_append(int argc, tchar **argv) const tchar *name; const tchar *desc; const tchar *flags_element = NULL; - WIMStruct *w = NULL; + WIMStruct *w; int ret; int cur_image; int cmd = tstrcmp(argv[0], T("append")) ? CAPTURE : APPEND; @@ -1124,13 +1287,13 @@ imagex_capture_or_append(int argc, tchar **argv) tchar *source_copy; const tchar *config_file = NULL; - tchar *config_str = NULL; - size_t config_len; + tchar *config_str; + struct wimlib_capture_config *config = NULL; bool source_list = false; size_t source_list_nchars; - tchar *source_list_contents = NULL; - bool capture_sources_malloced = false; + tchar *source_list_contents; + bool capture_sources_malloced; struct wimlib_capture_source *capture_sources; size_t num_sources; @@ -1225,7 +1388,7 @@ imagex_capture_or_append(int argc, tchar **argv) &num_sources); if (!capture_sources) { ret = -1; - goto out; + goto out_free_source_list_contents; } capture_sources_malloced = true; } else { @@ -1236,14 +1399,25 @@ imagex_capture_or_append(int argc, tchar **argv) capture_sources[0].wim_target_path = NULL; capture_sources[0].reserved = 0; num_sources = 1; + capture_sources_malloced = false; + source_list_contents = NULL; } if (config_file) { + size_t config_len; + config_str = file_get_text_contents(config_file, &config_len); if (!config_str) { ret = -1; - goto out; + goto out_free_capture_sources; } + + config = alloca(sizeof(*config)); + ret = parse_capture_config(&config_str, config_len, config); + if (ret) + goto out_free_config; + } else { + config = &default_capture_config; } if (cmd == APPEND) @@ -1251,8 +1425,8 @@ imagex_capture_or_append(int argc, tchar **argv) imagex_progress_func); else ret = wimlib_create_new_wim(compression_type, &w); - if (ret != 0) - goto out; + if (ret) + goto out_free_config; if (!source_list) { struct stat stbuf; @@ -1268,7 +1442,7 @@ imagex_capture_or_append(int argc, tchar **argv) imagex_error_with_errno(T("Failed to stat " "\"%"TS"\""), source); ret = -1; - goto out; + goto out_wimlib_free; } } } @@ -1276,12 +1450,11 @@ imagex_capture_or_append(int argc, tchar **argv) win32_acquire_capture_privileges(); #endif - ret = wimlib_add_image_multisource(w, capture_sources, - num_sources, name, - (config_str ? config_str : - default_capture_config), - (config_str ? config_len : - tstrlen(default_capture_config)), + ret = wimlib_add_image_multisource(w, + capture_sources, + num_sources, + name, + config, add_image_flags, imagex_progress_func); if (ret != 0) @@ -1313,12 +1486,20 @@ out_release_privs: #ifdef __WIN32__ win32_release_capture_privileges(); #endif -out: +out_wimlib_free: wimlib_free(w); - free(config_str); - free(source_list_contents); +out_free_config: + if (config != NULL && config != &default_capture_config) { + free(config->exclusion_pats.pats); + free(config->exclusion_exception_pats.pats); + free(config_str); + } +out_free_capture_sources: if (capture_sources_malloced) free(capture_sources); +out_free_source_list_contents: + free(source_list_contents); +out: return ret; } diff --git a/src/add_image.c b/src/add_image.c index 0e55a981..da11060b 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -159,7 +159,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, char *path, size_t path_len, struct wim_lookup_table *lookup_table, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func); @@ -168,7 +168,7 @@ unix_capture_directory(struct wim_dentry *dir_dentry, char *path, size_t path_len, struct wim_lookup_table *lookup_table, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func) { @@ -272,7 +272,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, char *path, size_t path_len, struct wim_lookup_table *lookup_table, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func) { @@ -420,7 +420,7 @@ out: * * @sd_set: Ignored. (Security data only captured in NTFS mode.) * - * @capture_config: + * @config: * Configuration for files to be excluded from capture. * * @add_flags: Bitwise or of WIMLIB_ADD_IMAGE_FLAG_* @@ -439,7 +439,7 @@ unix_build_dentry_tree(struct wim_dentry **root_ret, const char *root_disk_path, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, void *extra_arg) @@ -471,205 +471,17 @@ unix_build_dentry_tree(struct wim_dentry **root_ret, } #endif /* !__WIN32__ */ -enum pattern_type { - NONE = 0, - EXCLUSION_LIST, - EXCLUSION_EXCEPTION, - COMPRESSION_EXCLUSION_LIST, - ALIGNMENT_LIST, -}; - -#define COMPAT_DEFAULT_CONFIG - -/* Default capture configuration file when none is specified. */ -static const tchar *default_config = -#ifdef COMPAT_DEFAULT_CONFIG /* XXX: This policy is being moved to library - users. The next ABI-incompatible library - version will default to the empty string here. */ -T( -"[ExclusionList]\n" -"\\$ntfs.log\n" -"\\hiberfil.sys\n" -"\\pagefile.sys\n" -"\\System Volume Information\n" -"\\RECYCLER\n" -"\\Windows\\CSC\n" -); -#else -T(""); -#endif - -static void -destroy_pattern_list(struct pattern_list *list) -{ - FREE(list->pats); -} - -static void -destroy_capture_config(struct capture_config *config) -{ - destroy_pattern_list(&config->exclusion_list); - destroy_pattern_list(&config->exclusion_exception); - destroy_pattern_list(&config->compression_exclusion_list); - destroy_pattern_list(&config->alignment_list); - FREE(config->config_str); - memset(config, 0, sizeof(*config)); -} - -static int -pattern_list_add_pattern(struct pattern_list *list, const tchar *pattern) -{ - const tchar **pats; - if (list->num_pats >= list->num_allocated_pats) { - pats = REALLOC(list->pats, - sizeof(list->pats[0]) * (list->num_allocated_pats + 8)); - if (!pats) - return WIMLIB_ERR_NOMEM; - list->num_allocated_pats += 8; - list->pats = pats; - } - list->pats[list->num_pats++] = pattern; - return 0; -} - -/* Parses the contents of the image capture configuration file and fills in a - * `struct capture_config'. */ -static int -init_capture_config(struct capture_config *config, - const tchar *_config_str, - size_t config_num_tchars) -{ - tchar *config_str; - tchar *p; - tchar *eol; - tchar *next_p; - size_t num_tchars_remaining; - enum pattern_type type = NONE; - int ret; - unsigned long line_no = 0; - - DEBUG("config_num_tchars = %zu", config_num_tchars); - num_tchars_remaining = config_num_tchars; - memset(config, 0, sizeof(*config)); - config_str = TMALLOC(config_num_tchars); - if (!config_str) { - ERROR("Could not duplicate capture config string"); - return WIMLIB_ERR_NOMEM; - } - - tmemcpy(config_str, _config_str, config_num_tchars); - next_p = config_str; - config->config_str = config_str; - while (num_tchars_remaining != 0) { - line_no++; - p = next_p; - eol = tmemchr(p, T('\n'), num_tchars_remaining); - if (!eol) { - ERROR("Expected end-of-line in capture config file on " - "line %lu", line_no); - ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; - goto out_destroy; - } - - next_p = eol + 1; - num_tchars_remaining -= (next_p - p); - if (eol == p) - continue; - - if (*(eol - 1) == T('\r')) - eol--; - *eol = T('\0'); - - /* Translate backslash to forward slash */ - for (tchar *pp = p; pp != eol; pp++) - if (*pp == T('\\')) - *pp = T('/'); - - /* Check if the path begins with a drive letter */ - if (eol - p > 2 && *p != T('/') && *(p + 1) == T(':')) { - /* Don't allow relative paths on other drives */ - if (eol - p < 3 || *(p + 2) != T('/')) { - ERROR("Relative paths including a drive letter " - "are not allowed!\n" - " Perhaps you meant " - "\"%"TS":/%"TS"\"?\n", - *p, p + 2); - ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; - goto out_destroy; - } - #ifndef __WIN32__ - /* UNIX: strip the drive letter */ - p += 2; - #endif - } - - ret = 0; - if (!tstrcmp(p, T("[ExclusionList]"))) - type = EXCLUSION_LIST; - else if (!tstrcmp(p, T("[ExclusionException]"))) - type = EXCLUSION_EXCEPTION; - else if (!tstrcmp(p, T("[CompressionExclusionList]"))) - type = COMPRESSION_EXCLUSION_LIST; - else if (!tstrcmp(p, T("[AlignmentList]"))) - type = ALIGNMENT_LIST; - else if (p[0] == T('[') && tstrrchr(p, T(']'))) { - ERROR("Unknown capture configuration section \"%"TS"\"", p); - ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; - } else switch (type) { - case EXCLUSION_LIST: - DEBUG("Adding pattern \"%"TS"\" to exclusion list", p); - ret = pattern_list_add_pattern(&config->exclusion_list, p); - break; - case EXCLUSION_EXCEPTION: - DEBUG("Adding pattern \"%"TS"\" to exclusion exception list", p); - ret = pattern_list_add_pattern(&config->exclusion_exception, p); - break; - case COMPRESSION_EXCLUSION_LIST: - DEBUG("Adding pattern \"%"TS"\" to compression exclusion list", p); - ret = pattern_list_add_pattern(&config->compression_exclusion_list, p); - break; - case ALIGNMENT_LIST: - DEBUG("Adding pattern \"%"TS"\" to alignment list", p); - ret = pattern_list_add_pattern(&config->alignment_list, p); - break; - default: - ERROR("Line %lu of capture configuration is not " - "in a block (such as [ExclusionList])", - line_no); - ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; - break; - } - if (ret != 0) - goto out_destroy; - } - return 0; -out_destroy: - destroy_capture_config(config); - return ret; -} - -static bool -is_absolute_path(const tchar *path) -{ - if (*path == T('/')) - return true; -#ifdef __WIN32__ - /* Drive letter */ - if (*path && *(path + 1) == T(':')) - return true; -#endif - return false; -} - static bool match_pattern(const tchar *path, const tchar *path_basename, - const struct pattern_list *list) + const struct wimlib_pattern_list *list) { for (size_t i = 0; i < list->num_pats; i++) { + const tchar *pat = list->pats[i]; const tchar *string; - if (is_absolute_path(pat)) { + + if (*pat == T('/')) { /* Absolute path from root of capture */ string = path; } else { @@ -683,15 +495,18 @@ match_pattern(const tchar *path, /* Warning: on Windows native builds, fnmatch() calls the * replacement function in win32.c. */ - if (fnmatch(pat, string, FNM_PATHNAME + if (fnmatch(pat, string, FNM_PATHNAME | FNM_NOESCAPE #ifdef FNM_CASEFOLD | FNM_CASEFOLD #endif ) == 0) { - DEBUG("\"%"TS"\" matches the pattern \"%"TS"\"", + WARNING("\"%"TS"\" matches the pattern \"%"TS"\"", string, pat); return true; + } else { + WARNING("\"%"TS"\" does not match the pattern \"%"TS"\"", + string, pat); } } return false; @@ -708,19 +523,19 @@ match_pattern(const tchar *path, */ bool exclude_path(const tchar *path, size_t path_len, - const struct capture_config *config, bool exclude_prefix) + const struct wimlib_capture_config *config, bool exclude_prefix) { const tchar *basename = path_basename_with_len(path, path_len); if (exclude_prefix) { - wimlib_assert(path_len >= config->prefix_num_tchars); - if (!tmemcmp(config->prefix, path, config->prefix_num_tchars) && - path[config->prefix_num_tchars] == T('/')) + wimlib_assert(path_len >= config->_prefix_num_tchars); + if (!tmemcmp(config->_prefix, path, config->_prefix_num_tchars) && + path[config->_prefix_num_tchars] == T('/')) { - path += config->prefix_num_tchars; + path += config->_prefix_num_tchars; } } - return match_pattern(path, basename, &config->exclusion_list) && - !match_pattern(path, basename, &config->exclusion_exception); + return match_pattern(path, basename, &config->exclusion_pats) && + !match_pattern(path, basename, &config->exclusion_exception_pats); } @@ -977,13 +792,64 @@ attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, } } +static int +canonicalize_pat(tchar **pat_p) +{ + tchar *pat = *pat_p; + + /* Turn all backslashes in the pattern into forward slashes. */ + zap_backslashes(pat); + + if (*pat != T('/') && *pat != T('\0') && *(pat + 1) == T(':')) { + /* Pattern begins with drive letter */ + if (*(pat + 2) != T('/')) { + /* Something like c:file, which is actually a path + * relative to the current working directory on the c: + * drive. We require paths with drive letters to be + * absolute. */ + ERROR("Invalid path \"%"TS"\"; paths including drive letters " + "must be absolute!", pat); + ERROR("Maybe try \"%"TC":/%"TS"\"?", + *pat, pat + 2); + return WIMLIB_ERR_INVALID_CAPTURE_CONFIG; + } + + WARNING("Pattern \"%"TS"\" starts with a drive letter, which is " + "being removed.", pat); + /* Strip the drive letter */ + pat += 2; + *pat_p = pat; + } + return 0; +} + +static int +canonicalize_pat_list(struct wimlib_pattern_list *pat_list) +{ + int ret = 0; + for (size_t i = 0; i < pat_list->num_pats; i++) { + ret = canonicalize_pat(&pat_list->pats[i]); + if (ret) + break; + } + return ret; +} + +static int +canonicalize_capture_config(struct wimlib_capture_config *config) +{ + int ret = canonicalize_pat_list(&config->exclusion_pats); + if (ret) + return ret; + return canonicalize_pat_list(&config->exclusion_exception_pats); +} + WIMLIBAPI int wimlib_add_image_multisource(WIMStruct *w, struct wimlib_capture_source *sources, size_t num_sources, const tchar *name, - const tchar *config_str, - size_t config_len, + struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func) { @@ -991,7 +857,7 @@ wimlib_add_image_multisource(WIMStruct *w, const tchar *, struct wim_lookup_table *, struct sd_set *, - const struct capture_config *, + const struct wimlib_capture_config *, int, wimlib_progress_func_t, void *); @@ -999,7 +865,6 @@ wimlib_add_image_multisource(WIMStruct *w, struct wim_dentry *root_dentry; struct wim_dentry *branch; struct wim_security_data *sd; - struct capture_config config; struct wim_image_metadata *imd; int ret; struct sd_set sd_set; @@ -1061,12 +926,13 @@ wimlib_add_image_multisource(WIMStruct *w, return WIMLIB_ERR_IMAGE_NAME_COLLISION; } - if (!config_str) { - DEBUG("Using default capture configuration"); - config_str = default_config; - config_len = tstrlen(default_config); + if (!config) { + DEBUG("Capture config not provided; using empty config"); + config = alloca(sizeof(*config)); + memset(config, 0, sizeof(*config)); } - ret = init_capture_config(&config, config_str, config_len); + + ret = canonicalize_capture_config(config); if (ret) goto out; @@ -1074,7 +940,7 @@ wimlib_add_image_multisource(WIMStruct *w, sd = CALLOC(1, sizeof(struct wim_security_data)); if (!sd) { ret = WIMLIB_ERR_NOMEM; - goto out_destroy_capture_config; + goto out; } sd->total_length = 8; sd->refcnt = 1; @@ -1108,8 +974,8 @@ wimlib_add_image_multisource(WIMStruct *w, progress.scan.wim_target_path = sources[i].wim_target_path; progress_func(WIMLIB_PROGRESS_MSG_SCAN_BEGIN, &progress); } - config.prefix = sources[i].fs_source_path; - config.prefix_num_tchars = tstrlen(sources[i].fs_source_path); + config->_prefix = sources[i].fs_source_path; + config->_prefix_num_tchars = tstrlen(sources[i].fs_source_path); flags = add_image_flags | WIMLIB_ADD_IMAGE_FLAG_SOURCE; if (!*sources[i].wim_target_path) flags |= WIMLIB_ADD_IMAGE_FLAG_ROOT; @@ -1117,7 +983,7 @@ wimlib_add_image_multisource(WIMStruct *w, sources[i].fs_source_path, w->lookup_table, &sd_set, - &config, + config, flags, progress_func, extra_arg); if (ret) { @@ -1189,8 +1055,6 @@ out_free_security_data: free_security_data(sd); out_destroy_sd_set: destroy_sd_set(&sd_set); -out_destroy_capture_config: - destroy_capture_config(&config); out: return ret; } @@ -1199,8 +1063,7 @@ WIMLIBAPI int wimlib_add_image(WIMStruct *w, const tchar *source, const tchar *name, - const tchar *config_str, - size_t config_len, + struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func) { @@ -1215,8 +1078,8 @@ wimlib_add_image(WIMStruct *w, .reserved = 0, }; ret = wimlib_add_image_multisource(w, &capture_src, 1, name, - config_str, config_len, - add_image_flags, progress_func); + config, add_image_flags, + progress_func); FREE(fs_source_path); return ret; } diff --git a/src/ntfs-capture.c b/src/ntfs-capture.c index 03c8720a..2647e217 100644 --- a/src/ntfs-capture.c +++ b/src/ntfs-capture.c @@ -396,7 +396,7 @@ struct readdir_ctx { struct wim_lookup_table *lookup_table; struct sd_set *sd_set; struct dos_name_map *dos_name_map; - const struct capture_config *config; + const struct wimlib_capture_config *config; ntfs_volume **ntfs_vol_p; int add_image_flags; wimlib_progress_func_t progress_func; @@ -411,7 +411,7 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_p, int name_type, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, ntfs_volume **ntfs_vol_p, int add_image_flags, wimlib_progress_func_t progress_func); @@ -499,7 +499,7 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_p, int name_type, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, ntfs_volume **ntfs_vol_p, int add_image_flags, wimlib_progress_func_t progress_func) @@ -642,7 +642,7 @@ build_dentry_tree_ntfs(struct wim_dentry **root_p, const char *device, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, void *extra_arg) diff --git a/src/wimlib.h b/src/wimlib.h index dfa685f0..45221872 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -605,6 +605,46 @@ struct wimlib_capture_source { long reserved; }; +/** Structure that specifies a list of path patterns. */ +struct wimlib_pattern_list { + /** Array of patterns. The patterns may be modified by library code, + * but the @a pats pointer itself will not. See the man page for + * wimlib-imagex capture for more information about allowed + * patterns. */ + tchar **pats; + + /** Number of patterns in the @a pats array. */ + size_t num_pats; + + /** Ignored; may be used by the calling code. */ + size_t num_allocated_pats; +}; + +/** A structure that contains lists of wildcards that match paths to treat + * specially when capturing a WIM image. */ +struct wimlib_capture_config { + /** Paths matching any pattern this list are excluded from being + * captured, except if the same path appears in @a + * exclusion_exception_pats. */ + struct wimlib_pattern_list exclusion_pats; + + /** Paths matching any pattern in this list are never excluded from + * being captured. */ + struct wimlib_pattern_list exclusion_exception_pats; + + /** Reserved for future capture configuration options. */ + struct wimlib_pattern_list reserved1; + + /** Reserved for future capture configuration options. */ + struct wimlib_pattern_list reserved2; + + /** Library internal use only. */ + tchar *_prefix; + + /** Library internal use only. */ + size_t _prefix_num_tchars; +}; + /***************************** * WIMLIB_ADD_IMAGE_FLAG_* * @@ -857,8 +897,7 @@ struct wimlib_modify_command { * added at the end to maintain a compatible ABI, except when it's being broken * anyway. */ enum wimlib_error_code { - WIMLIB_ERR_SUCCESS = 0, - WIMLIB_ERR_ALREADY_LOCKED, + WIMLIB_ERR_ALREADY_LOCKED = 1, WIMLIB_ERR_COMPRESSED_LOOKUP_TABLE, WIMLIB_ERR_DECOMPRESSION, WIMLIB_ERR_DELETE_STAGING_DIR, @@ -878,14 +917,16 @@ enum wimlib_error_code { WIMLIB_ERR_INVALID_IMAGE, WIMLIB_ERR_INVALID_INTEGRITY_TABLE, WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY, + WIMLIB_ERR_INVALID_MULTIBYTE_STRING, + WIMLIB_ERR_INVALID_OVERLAY, WIMLIB_ERR_INVALID_PARAM, WIMLIB_ERR_INVALID_PART_NUMBER, WIMLIB_ERR_INVALID_RESOURCE_HASH, WIMLIB_ERR_INVALID_RESOURCE_SIZE, WIMLIB_ERR_INVALID_SECURITY_DATA, WIMLIB_ERR_INVALID_UNMOUNT_MESSAGE, - WIMLIB_ERR_INVALID_UTF8_STRING, WIMLIB_ERR_INVALID_UTF16_STRING, + WIMLIB_ERR_INVALID_UTF8_STRING, WIMLIB_ERR_LIBXML_UTF16_HANDLER_NOT_AVAILABLE, WIMLIB_ERR_LINK, WIMLIB_ERR_MKDIR, @@ -897,8 +938,8 @@ enum wimlib_error_code { WIMLIB_ERR_NTFS_3G, WIMLIB_ERR_OPEN, WIMLIB_ERR_OPENDIR, - WIMLIB_ERR_READLINK, WIMLIB_ERR_READ, + WIMLIB_ERR_READLINK, WIMLIB_ERR_RENAME, WIMLIB_ERR_REOPEN, WIMLIB_ERR_RESOURCE_ORDER, @@ -906,14 +947,13 @@ enum wimlib_error_code { WIMLIB_ERR_SPLIT_INVALID, WIMLIB_ERR_SPLIT_UNSUPPORTED, WIMLIB_ERR_STAT, + WIMLIB_ERR_SUCCESS = 0, WIMLIB_ERR_TIMEOUT, + WIMLIB_ERR_UNICODE_STRING_NOT_REPRESENTABLE, WIMLIB_ERR_UNKNOWN_VERSION, WIMLIB_ERR_UNSUPPORTED, WIMLIB_ERR_WRITE, WIMLIB_ERR_XML, - WIMLIB_ERR_INVALID_OVERLAY, - WIMLIB_ERR_INVALID_MULTIBYTE_STRING, - WIMLIB_ERR_UNICODE_STRING_NOT_REPRESENTABLE, }; @@ -950,12 +990,9 @@ enum wimlib_error_code { * @param name * The name to give the image. This must be non-@c NULL. * @param config - * Pointer to the contents of an image capture configuration file. If @c - * NULL, a default string is used. Please see the manual page for - * wimlib-imagex capture for more information. - * @param config_len - * Length of the string @a config in bytes, not including an optional - * null-terminator. Ignored if @a config is @c NULL. + * Capture configuration that specifies files, directories, or path globs + * to exclude from being captured. If @c NULL, a dummy configuration where + * no paths are treated specially is used. * @param add_image_flags * Bitwise OR of flags prefixed with WIMLIB_ADD_IMAGE_FLAG. * @param progress_func @@ -1010,8 +1047,7 @@ extern int wimlib_add_image(WIMStruct *wim, const wimlib_tchar *source, const wimlib_tchar *name, - const wimlib_tchar *config, - size_t config_len, + struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func); @@ -1042,8 +1078,7 @@ wimlib_add_image_multisource(WIMStruct *w, struct wimlib_capture_source *sources, size_t num_sources, const wimlib_tchar *name, - const wimlib_tchar *config_str, - size_t config_len, + struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func); diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index 3b849983..6a6bc083 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -351,24 +351,9 @@ resource_is_compressed(const struct resource_entry *entry) /* add_image.c */ -struct pattern_list { - const tchar **pats; - size_t num_pats; - size_t num_allocated_pats; -}; - -struct capture_config { - struct pattern_list exclusion_list; - struct pattern_list exclusion_exception; - struct pattern_list compression_exclusion_list; - struct pattern_list alignment_list; - tchar *config_str; - tchar *prefix; - size_t prefix_num_tchars; -}; extern bool exclude_path(const tchar *path, size_t path_len, - const struct capture_config *config, + const struct wimlib_capture_config *config, bool exclude_prefix); extern int @@ -477,7 +462,7 @@ build_dentry_tree_ntfs(struct wim_dentry **root_p, const tchar *device, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, void *extra_arg); diff --git a/src/win32.c b/src/win32.c index 57f6fdb9..3dc20a94 100644 --- a/src/win32.c +++ b/src/win32.c @@ -310,7 +310,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, size_t path_num_chars, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, struct win32_capture_state *state); @@ -323,7 +323,7 @@ win32_recurse_directory(struct wim_dentry *root, size_t dir_path_num_chars, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, struct win32_capture_state *state) @@ -734,7 +734,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, size_t path_num_chars, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, struct win32_capture_state *state) @@ -911,7 +911,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, const wchar_t *root_disk_path, struct wim_lookup_table *lookup_table, struct sd_set *sd_set, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, void *extra_arg) diff --git a/src/win32.h b/src/win32.h index 59cab518..82cdeef3 100644 --- a/src/win32.h +++ b/src/win32.h @@ -10,7 +10,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, const tchar *root_disk_path, struct wim_lookup_table *lookup_table, struct sd_set *sd, - const struct capture_config *config, + const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, void *extra_arg); @@ -34,6 +34,7 @@ extern void win32_error_last(); #endif #define FNM_PATHNAME 0x1 +#define FNM_NOESCAPE 0x2 #define FNM_NOMATCH 1 extern int fnmatch(const tchar *pattern, const tchar *string, int flags);