From 1fc939b7bd0b37900d974b1cd5b11df128df71f5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 27 Dec 2013 22:43:24 -0600 Subject: [PATCH] Allow configurable case sensitivity Set WIMLIB_IMAGEX_IGNORE_CASE for wimlib-imagex, or pass WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE or WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE to wimlib_global_init(). --- doc/imagex-extract.1.in | 8 +- doc/imagex.1.in | 40 +++-- include/wimlib.h | 13 +- include/wimlib/dentry.h | 33 ++-- include/wimlib/encoding.h | 8 + include/wimlib/wildcard.h | 7 +- include/wimlib/wim.h | 3 - include/wimlib/win32.h | 6 - include/wimlib_tchar.h | 2 + programs/imagex.c | 23 ++- src/dentry.c | 319 ++++++++++++++++++++++---------------- src/encoding.c | 147 ++++++++++++++++++ src/extract.c | 4 +- src/lookup_table.c | 2 +- src/lzms-compress.c | 25 +-- src/lzms-decompress.c | 10 +- src/mount_image.c | 29 +++- src/ntfs-3g_capture.c | 4 +- src/resource.c | 4 +- src/unix_capture.c | 2 +- src/update_image.c | 90 ++--------- src/wildcard.c | 76 +++++++-- src/wim.c | 18 ++- src/win32_capture.c | 2 +- src/win32_replacements.c | 1 + 25 files changed, 560 insertions(+), 316 deletions(-) diff --git a/doc/imagex-extract.1.in b/doc/imagex-extract.1.in index 6d21cee4..2b9afc18 100644 --- a/doc/imagex-extract.1.in +++ b/doc/imagex-extract.1.in @@ -40,7 +40,9 @@ Each such path must be specified as an absolute path starting from the root of the WIM image, like those output by the \fB@IMAGEX_PROGNAME@ dir\fR (1) command. However, path separators may be either forward or backward slashes, and the leading slash is optional; also, on Windows, the paths are treated -case-insensitively, while on UNIX, paths are treated case-sensitively. +case-insensitively, while on UNIX, paths are treated case-sensitively, except +when overwritten through the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental +variable, as documented in \fB@IMAGEX_PROGNAME@\fR (1). .PP If no \fIPATH\fRs are provided, the default behavior is to extract the full image, as if the path "/" had been provided. @@ -104,10 +106,6 @@ Do not interpret wildcard characters in paths in the \fILISTFILE\fR. \fB--strict-wildcards\fR Fail if any wildcards or paths in \fILISTFILE\fR do not match any files in the WIM image. The default behavior is to warn only. -.TP -\fB--case-insensitive-wildcards\fR -Treat the wildcards or paths in \fILISTFILE\fR as case-insensitive. On Windows -this is already the default behavior, but on UNIX-like systems it is not. .SH NOTES See the documentation \fB@IMAGEX_PROGNAME@ apply\fR (1) for documentation about what data and metadata are extracted on UNIX-like systems versus on Windows. diff --git a/doc/imagex.1.in b/doc/imagex.1.in index 29508753..7f13df5e 100644 --- a/doc/imagex.1.in +++ b/doc/imagex.1.in @@ -170,17 +170,39 @@ mounting an image from a split WIM, but Microsoft's software does not. (Note: this functionality is not available in Windows builds of wimlib and \fB@IMAGEX_PROGNAME@\fR.) .SH LOCALES AND CHARACTER ENCODINGS -On Windows, wimlib works in UTF-16LE, and there should be no problems with -character encodings. +WIM files themselves store file and stream names using the UTF16-LE. On +Windows, wimlib works in UTF-16LE, so conversions are usually necessary and +there should be no problems with character encodings, except possibly in the XML +data. .PP -On UNIX, wimlib works primarily in the locale-dependent multibyte encoding, -which you are strongly recommended to set to UTF-8 to avoid any problems. +On UNIX-like systems, wimlib works primarily in the locale-dependent multibyte +encoding, which you are strongly recommended to set to UTF-8 to avoid any +problems. You can alternatively set the environmental variable +\fBWIMLIB_IMAGEX_USE_UTF8\fR to force \fB@IMAGEX_PROGNAME@\fR to use UTF-8 +character encoding internally, even if the current locale is not UTF-8 +compatible. .SH CASE SENSITIVITY -The case sensitivity of \fB@IMAGEX_PROGNAME@\fR differs somewhat between -UNIX-like systems and Windows. Filenames are internally treated as -case-sensitive, but on Windows paths actually provided by the user will be -treated as case-insensitive in order to get the "expected" behavior. Otherwise, -options and non-path arguments should be specified in lower case. +By default, the case sensitivity of \fB@IMAGEX_PROGNAME@\fR differs somewhat +between UNIX-like systems and Windows. WIM images may (but usually do not) have +multiple files with the same case-insensitive name. Internally, wimlib +stores filenames as case-sensitive, but on Windows paths +actually provided by the user for use in a WIM image (e.g. for extracting, +adding, renaming, or deleting files) will be treated as case-insensitive in +order to get the "expected" behavior. This differs from the default behavior on +UNIX-like systems, where such paths will be treated as case-sensitive. Note +that with case insensitively, a path component may in general be ambiguous due +to multiple files or directories having the same case-insensitive name. In such +cases, if there is a file or directory with an exactly matching name, it is +chosen; otherwise, one of the case-insensitively matching file or directories is +chosen arbitrarily. +.PP +The default behavior can be overwritten by explicitly setting the environmental +variable \fBWIMLIB_IMAGEX_IGNORE_CASE\fR to 1, in which case such paths will be +treated case insensitively, or 0, in which such paths will be treated case +sensitsively. +.PP +Regardless of these settings, options and non-path arguments must be specified +in lower case. .SH LICENSE wimlib and \fB@IMAGEX_PROGNAME@\fR are distributed under the GNU General Public License version 3 or later. Be aware this means this software is provided as-is diff --git a/include/wimlib.h b/include/wimlib.h index 512c6149..f627044f 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -1397,11 +1397,6 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour * one of the provided globs did not match a file. */ #define WIMLIB_EXTRACT_FLAG_STRICT_GLOB 0x00080000 -/** In combination with ::WIMLIB_EXTRACT_FLAG_GLOB_PATHS, causes the globbing to - * be performed case insensitively. On Windows this is already the default - * behavior but on UNIX-like systems it is not. */ -#define WIMLIB_EXTRACT_FLAG_CASE_INSENSITIVE_GLOB 0x00100000 - /** @} */ /** @ingroup G_mounting_wim_images * @{ */ @@ -1610,6 +1605,14 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour */ #define WIMLIB_INIT_FLAG_STRICT_APPLY_PRIVILEGES 0x00000008 +/** Default to interpreting WIM paths case sensitively (default on UNIX-like + * systems). */ +#define WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE 0x00000010 + +/** Default to interpreting WIM paths case insensitively (default on Windows). + * This does not apply to mounted images. */ +#define WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE 0x00000020 + /** @} */ /** @ingroup G_nonstandalone_wims * @{ */ diff --git a/include/wimlib/dentry.h b/include/wimlib/dentry.h index f5140281..22f0aff5 100644 --- a/include/wimlib/dentry.h +++ b/include/wimlib/dentry.h @@ -131,7 +131,6 @@ struct wim_dentry { * case sensitive long name. */ struct rb_node rb_node; -#ifdef __WIN32__ /* Node for the parent's red-black tree of child dentries, sorted by * case insensitive long name. */ struct rb_node rb_node_case_insensitive; @@ -139,7 +138,6 @@ struct wim_dentry { /* List of dentries in a directory that have different case sensitive * long names but share the same case insensitive long name */ struct list_head case_insensitive_conflict_list; -#endif /* Length of UTF-16LE encoded short filename, in bytes, not including * the terminating zero wide-character. */ @@ -269,11 +267,9 @@ struct wim_inode { * any. Keyed by wim_dentry->file_name, case sensitively. */ struct rb_root i_children; -#ifdef __WIN32__ /* Root of a red-black tree storing the children of this inode, if any. * Keyed by wim_dentry->file_name, case insensitively. */ struct rb_root i_children_case_insensitive; -#endif /* List of dentries that are aliases for this inode. There will be * i_nlink dentries in this list. */ @@ -448,23 +444,34 @@ calculate_subdir_offsets(struct wim_dentry *dentry, u64 *subdir_offset_p); extern int set_dentry_name(struct wim_dentry *dentry, const tchar *new_name); -extern struct wim_dentry * -get_dentry(struct WIMStruct *wim, const tchar *path); -extern struct wim_inode * -wim_pathname_to_inode(struct WIMStruct *wim, const tchar *path); +typedef enum { + /* NTFS-3g headers define CASE_SENSITIVE... */ + WIMLIB_CASE_PLATFORM_DEFAULT = 0, + WIMLIB_CASE_SENSITIVE = 1, + WIMLIB_CASE_INSENSITIVE = 2, +} CASE_SENSITIVITY_TYPE; + +extern bool default_ignore_case; + +extern struct wim_dentry * +get_dentry(struct WIMStruct *wim, const tchar *path, + CASE_SENSITIVITY_TYPE case_type); extern struct wim_dentry * get_dentry_child_with_name(const struct wim_dentry *dentry, - const tchar *name); + const tchar *name, + CASE_SENSITIVITY_TYPE case_type); extern struct wim_dentry * get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry, const utf16lechar *name, - size_t name_nbytes); + size_t name_nbytes, + CASE_SENSITIVITY_TYPE case_type); extern struct wim_dentry * -get_parent_dentry(struct WIMStruct *wim, const tchar *path); +get_parent_dentry(struct WIMStruct *wim, const tchar *path, + CASE_SENSITIVITY_TYPE case_type); extern int print_dentry(struct wim_dentry *dentry, void *lookup_table); @@ -519,6 +526,10 @@ extern struct wim_dentry * dentry_add_child(struct wim_dentry * restrict parent, struct wim_dentry * restrict child); +extern int +rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to, + CASE_SENSITIVITY_TYPE case_type); + extern struct wim_ads_entry * inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, u16 *idx_ret); diff --git a/include/wimlib/encoding.h b/include/wimlib/encoding.h index a621581c..59914ae3 100644 --- a/include/wimlib/encoding.h +++ b/include/wimlib/encoding.h @@ -6,6 +6,9 @@ extern void iconv_global_cleanup(void); +extern void +init_upcase(void); + extern bool wimlib_mbs_is_utf8; #define DECLARE_CHAR_CONVERSION_FUNCTIONS(varname1, varname2, \ @@ -39,4 +42,9 @@ utf8_to_tstr_simple(const char *utf8str, tchar **out); extern int tstr_to_utf8_simple(const tchar *tstr, char **out); +extern int +cmp_utf16le_strings(const utf16lechar *s1, size_t n1, + const utf16lechar *s2, size_t n2, + bool ignore_case); + #endif /* _WIMLIB_ENCODING_H */ diff --git a/include/wimlib/wildcard.h b/include/wimlib/wildcard.h index 764f4786..4506ac3f 100644 --- a/include/wimlib/wildcard.h +++ b/include/wimlib/wildcard.h @@ -10,7 +10,7 @@ extern int expand_wildcard_wim_paths(WIMStruct *wim, - const char * const *wildcards, + const tchar * const *wildcards, size_t num_wildcards, tchar ***expanded_paths_ret, size_t *num_expanded_paths_ret, @@ -19,7 +19,10 @@ expand_wildcard_wim_paths(WIMStruct *wim, #ifdef __WIN32__ extern int fnmatch(const tchar *pattern, const tchar *string, int flags); -# define FNM_CASEFOLD 0 +# define FNM_CASEFOLD 0x1 +# define FNM_PATHNAME 0x2 +# define FNM_NOESCAPE 0x4 +# define FNM_NOMATCH 1 #else # include # ifndef FNM_CASEFOLD diff --git a/include/wimlib/wim.h b/include/wimlib/wim.h index f045c885..3fdc7d2d 100644 --- a/include/wimlib/wim.h +++ b/include/wimlib/wim.h @@ -133,9 +133,6 @@ write_wim_header_at_offset(const struct wim_header *hdr, struct filedes *out_fd, extern int write_wim_header_flags(u32 hdr_flags, struct filedes *out_fd); -extern int -rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to); - extern int select_wim_image(WIMStruct *wim, int image); diff --git a/include/wimlib/win32.h b/include/wimlib/win32.h index 6bb5b34b..05017439 100644 --- a/include/wimlib/win32.h +++ b/include/wimlib/win32.h @@ -30,12 +30,6 @@ win32_global_init(int init_flags); extern void win32_global_cleanup(void); -#define FNM_PATHNAME 0x1 -#define FNM_NOESCAPE 0x2 -#define FNM_NOMATCH 1 -extern int -fnmatch(const tchar *pattern, const tchar *string, int flags); - extern int fsync(int fd); diff --git a/include/wimlib_tchar.h b/include/wimlib_tchar.h index 5b923100..33ad0cc5 100644 --- a/include/wimlib_tchar.h +++ b/include/wimlib_tchar.h @@ -53,6 +53,7 @@ typedef wchar_t tchar; # define taccess _waccess # define tstrdup wcsdup # define ttempnam _wtempnam +# define tgetenv _wgetenv /* The following "tchar" functions do not have exact wide-character equivalents * on Windows so require parameter rearrangement or redirection to a replacement * function defined ourselves. */ @@ -114,6 +115,7 @@ typedef char tchar; # define tmkdir mkdir # define tstrdup strdup # define ttempnam tempnam +# define tgetenv getenv # define TSTRDUP STRDUP # define tstrerror_r strerror_r # define trename rename diff --git a/programs/imagex.c b/programs/imagex.c index ce5ac8d5..aa628b35 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -119,7 +119,6 @@ static FILE *imagex_info_file; enum { IMAGEX_ALLOW_OTHER_OPTION, IMAGEX_BOOT_OPTION, - IMAGEX_CASE_INSENSITIVE_WILDCARDS_OPTION, IMAGEX_CHECK_OPTION, IMAGEX_CHUNK_SIZE_OPTION, IMAGEX_COMMAND_OPTION, @@ -257,7 +256,6 @@ static const struct option extract_options[] = { {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION}, {T("strict-wildcards"), no_argument, NULL, IMAGEX_STRICT_WILDCARDS_OPTION}, {T("no-wildcards"), no_argument, NULL, IMAGEX_NO_WILDCARDS_OPTION}, - {T("case-insensitive-wildcards"), no_argument, NULL, IMAGEX_CASE_INSENSITIVE_WILDCARDS_OPTION}, {NULL, 0, NULL, 0}, }; @@ -2750,9 +2748,6 @@ imagex_extract(int argc, tchar **argv, int cmd) case IMAGEX_NO_WILDCARDS_OPTION: listfile_extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS; break; - case IMAGEX_CASE_INSENSITIVE_WILDCARDS_OPTION: - listfile_extract_flags |= WIMLIB_EXTRACT_FLAG_CASE_INSENSITIVE_GLOB; - break; case IMAGEX_STRICT_WILDCARDS_OPTION: listfile_extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_GLOB; break; @@ -4091,8 +4086,26 @@ main(int argc, char **argv) } } + #endif /* !__WIN32__ */ + { + tchar *igcase = tgetenv(T("WIMLIB_IMAGEX_IGNORE_CASE")); + if (igcase != NULL) { + if (!tstrcmp(igcase, T("no")) || + !tstrcmp(igcase, T("0"))) + init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE; + else if (!tstrcmp(igcase, T("yes")) || + !tstrcmp(igcase, T("1"))) + init_flags |= WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE; + else { + fprintf(stderr, + "WARNING: Ignoring unknown setting of " + "WIMLIB_IMAGEX_IGNORE_CASE\n"); + } + } + } + /* Allow being invoked as wimCOMMAND (e.g. wimapply). */ cmd = CMD_NONE; if (!tstrncmp(invocation_name, T("wim"), 3) && diff --git a/src/dentry.c b/src/dentry.c index 53a44cf1..ad78f7db 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -37,6 +37,7 @@ #include "wimlib/error.h" #include "wimlib/lookup_table.h" #include "wimlib/metadata.h" +#include "wimlib/paths.h" #include "wimlib/resource.h" #include "wimlib/security.h" #include "wimlib/sha1.h" @@ -634,141 +635,129 @@ calculate_subdir_offsets(struct wim_dentry *dentry, u64 *subdir_offset_p) } } -/* Case-sensitive UTF-16LE dentry or stream name comparison. Used on both UNIX - * (always) and Windows (sometimes) */ -static int -compare_utf16le_names_case_sensitive(const utf16lechar *name1, size_t nbytes1, - const utf16lechar *name2, size_t nbytes2) -{ - /* Return the result if the strings differ up to their minimum length. - * Note that we cannot use strcmp() or strncmp() here, as the strings - * are in UTF-16LE format. */ - int result = memcmp(name1, name2, min(nbytes1, nbytes2)); - if (result) - return result; - - /* The strings are the same up to their minimum length, so return a - * result based on their lengths. */ - if (nbytes1 < nbytes2) - return -1; - else if (nbytes1 > nbytes2) - return 1; - else - return 0; -} - -#ifdef __WIN32__ -/* Windoze: Case-insensitive UTF-16LE dentry or stream name comparison */ -static int -compare_utf16le_names_case_insensitive(const utf16lechar *name1, size_t nbytes1, - const utf16lechar *name2, size_t nbytes2) -{ - /* Return the result if the strings differ up to their minimum length. - * */ - int result = _wcsnicmp((const wchar_t*)name1, (const wchar_t*)name2, - min(nbytes1 / 2, nbytes2 / 2)); - if (result) - return result; - - /* The strings are the same up to their minimum length, so return a - * result based on their lengths. */ - if (nbytes1 < nbytes2) - return -1; - else if (nbytes1 > nbytes2) - return 1; - else - return 0; -} -#endif /* __WIN32__ */ - -#ifdef __WIN32__ -# define compare_utf16le_names compare_utf16le_names_case_insensitive -#else -# define compare_utf16le_names compare_utf16le_names_case_sensitive -#endif - - -#ifdef __WIN32__ static int dentry_compare_names_case_insensitive(const struct wim_dentry *d1, const struct wim_dentry *d2) { - return compare_utf16le_names_case_insensitive(d1->file_name, - d1->file_name_nbytes, - d2->file_name, - d2->file_name_nbytes); + return cmp_utf16le_strings(d1->file_name, + d1->file_name_nbytes / 2, + d2->file_name, + d2->file_name_nbytes / 2, + true); } -#endif /* __WIN32__ */ static int dentry_compare_names_case_sensitive(const struct wim_dentry *d1, const struct wim_dentry *d2) { - return compare_utf16le_names_case_sensitive(d1->file_name, - d1->file_name_nbytes, - d2->file_name, - d2->file_name_nbytes); + return cmp_utf16le_strings(d1->file_name, + d1->file_name_nbytes / 2, + d2->file_name, + d2->file_name_nbytes / 2, + false); } -#ifdef __WIN32__ -# define dentry_compare_names dentry_compare_names_case_insensitive -#else -# define dentry_compare_names dentry_compare_names_case_sensitive -#endif - /* Return %true iff the alternate data stream entry @entry has the UTF-16LE * stream name @name that has length @name_nbytes bytes. */ static inline bool ads_entry_has_name(const struct wim_ads_entry *entry, - const utf16lechar *name, size_t name_nbytes) + const utf16lechar *name, size_t name_nbytes, + bool ignore_case) { - return !compare_utf16le_names(name, name_nbytes, - entry->stream_name, - entry->stream_name_nbytes); + return 0 == cmp_utf16le_strings(name, + name_nbytes / 2, + entry->stream_name, + entry->stream_name_nbytes / 2, + ignore_case); } +bool default_ignore_case = +#ifdef __WIN32__ + true +#else + false +#endif +; + +static bool +will_ignore_case(CASE_SENSITIVITY_TYPE case_type) +{ + if (case_type == WIMLIB_CASE_SENSITIVE) + return false; + if (case_type == WIMLIB_CASE_INSENSITIVE) + return true; + + return default_ignore_case; +} + + /* Given a UTF-16LE filename and a directory, look up the dentry for the file. * Return it if found, otherwise NULL. This is case-sensitive on UNIX and * case-insensitive on Windows. */ struct wim_dentry * get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry, const utf16lechar *name, - size_t name_nbytes) + size_t name_nbytes, + CASE_SENSITIVITY_TYPE case_ctype) { struct rb_node *node; -#ifdef __WIN32__ - node = dentry->d_inode->i_children_case_insensitive.rb_node; -#else - node = dentry->d_inode->i_children.rb_node; -#endif + bool ignore_case = will_ignore_case(case_ctype); + + if (ignore_case) + node = dentry->d_inode->i_children_case_insensitive.rb_node; + else + node = dentry->d_inode->i_children.rb_node; struct wim_dentry *child; while (node) { - #ifdef __WIN32__ - child = rb_entry(node, struct wim_dentry, rb_node_case_insensitive); - #else - child = rbnode_dentry(node); - #endif - int result = compare_utf16le_names(name, name_nbytes, - child->file_name, - child->file_name_nbytes); - if (result < 0) + if (ignore_case) + child = rb_entry(node, struct wim_dentry, rb_node_case_insensitive); + else + child = rb_entry(node, struct wim_dentry, rb_node); + + int result = cmp_utf16le_strings(name, + name_nbytes / 2, + child->file_name, + child->file_name_nbytes / 2, + ignore_case); + if (result < 0) { node = node->rb_left; - else if (result > 0) + } else if (result > 0) { node = node->rb_right; - else { - #ifdef __WIN32__ - if (!list_empty(&child->case_insensitive_conflict_list)) - { - WARNING("Result of case-insensitive lookup is ambiguous " - "(returning \"%ls\" instead of \"%ls\")", - child->file_name, - container_of(child->case_insensitive_conflict_list.next, - struct wim_dentry, - case_insensitive_conflict_list)->file_name); - } - #endif + } else if (!ignore_case || + list_empty(&child->case_insensitive_conflict_list)) { + return child; + } else { + /* Multiple dentries have the same case-insensitive + * name, and a case-insensitive lookup is being + * performed. Choose the dentry with the same + * case-sensitive name, if one exists; otherwise print a + * warning and choose one arbitrarily. */ + struct wim_dentry *alt = child; + size_t num_alts = 0; + + do { + num_alts++; + if (0 == cmp_utf16le_strings(name, + name_nbytes / 2, + alt->file_name, + alt->file_name_nbytes / 2, + false)) + return alt; + alt = list_entry(alt->case_insensitive_conflict_list.next, + struct wim_dentry, + case_insensitive_conflict_list); + } while (alt != child); + + WARNING("Result of case-insensitive lookup is ambiguous\n" + " (returning \"%"TS"\" of %zu " + "possible files, including \"%"TS"\")", + dentry_full_path(child), + num_alts, + dentry_full_path(list_entry(child->case_insensitive_conflict_list.next, + struct wim_dentry, + case_insensitive_conflict_list))); return child; } } @@ -778,11 +767,13 @@ get_dentry_child_with_utf16le_name(const struct wim_dentry *dentry, /* Returns the child of @dentry that has the file name @name. Returns NULL if * no child has the name. */ struct wim_dentry * -get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name) +get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name, + CASE_SENSITIVITY_TYPE case_type) { #if TCHAR_IS_UTF16LE return get_dentry_child_with_utf16le_name(dentry, name, - tstrlen(name) * sizeof(tchar)); + tstrlen(name) * sizeof(tchar), + case_type); #else utf16lechar *utf16le_name; size_t utf16le_name_nbytes; @@ -796,7 +787,8 @@ get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name) } else { child = get_dentry_child_with_utf16le_name(dentry, utf16le_name, - utf16le_name_nbytes); + utf16le_name_nbytes, + case_type); FREE(utf16le_name); } return child; @@ -804,7 +796,8 @@ get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name) } static struct wim_dentry * -get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path) +get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path, + CASE_SENSITIVITY_TYPE case_type) { struct wim_dentry *cur_dentry, *parent_dentry; const utf16lechar *p, *pp; @@ -826,7 +819,8 @@ get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path) pp++; cur_dentry = get_dentry_child_with_utf16le_name(parent_dentry, p, - (void*)pp - (void*)p); + (u8*)pp - (u8*)p, + case_type); if (cur_dentry == NULL) break; p = pp; @@ -844,14 +838,12 @@ get_dentry_utf16le(WIMStruct *wim, const utf16lechar *path) /* * Returns the dentry in the currently selected WIM image named by @path * starting from the root of the WIM image, or NULL if there is no such dentry. - * - * On Windows, the search is done case-insensitively. */ struct wim_dentry * -get_dentry(WIMStruct *wim, const tchar *path) +get_dentry(WIMStruct *wim, const tchar *path, CASE_SENSITIVITY_TYPE case_type) { #if TCHAR_IS_UTF16LE - return get_dentry_utf16le(wim, path); + return get_dentry_utf16le(wim, path, case_type); #else utf16lechar *path_utf16le; size_t path_utf16le_nbytes; @@ -862,23 +854,12 @@ get_dentry(WIMStruct *wim, const tchar *path) &path_utf16le, &path_utf16le_nbytes); if (ret) return NULL; - dentry = get_dentry_utf16le(wim, path_utf16le); + dentry = get_dentry_utf16le(wim, path_utf16le, case_type); FREE(path_utf16le); return dentry; #endif } -struct wim_inode * -wim_pathname_to_inode(WIMStruct *wim, const tchar *path) -{ - struct wim_dentry *dentry; - dentry = get_dentry(wim, path); - if (dentry) - return dentry->d_inode; - else - return NULL; -} - /* Takes in a path of length @len in @buf, and transforms it into a string for * the path of its parent directory. */ static void @@ -897,14 +878,15 @@ to_parent_name(tchar *buf, size_t len) /* Returns the dentry that corresponds to the parent directory of @path, or NULL * if the dentry is not found. */ struct wim_dentry * -get_parent_dentry(WIMStruct *wim, const tchar *path) +get_parent_dentry(WIMStruct *wim, const tchar *path, + CASE_SENSITIVITY_TYPE case_type) { size_t path_len = tstrlen(path); tchar buf[path_len + 1]; tmemcpy(buf, path, path_len + 1); to_parent_name(buf, path_len); - return get_dentry(wim, buf); + return get_dentry(wim, buf, case_type); } /* Prints the full path of a dentry. */ @@ -1270,8 +1252,6 @@ free_dentry_tree(struct wim_dentry *root, struct wim_lookup_table *lookup_table) for_dentry_in_tree_depth(root, do_free_dentry, lookup_table); } -#ifdef __WIN32__ - /* Insert a dentry into the case insensitive index for a directory. * * This is a red-black tree, but when multiple dentries share the same @@ -1307,7 +1287,6 @@ dentry_add_child_case_insensitive(struct wim_dentry *parent, rb_insert_color(&child->rb_node_case_insensitive, root); return NULL; } -#endif /* * Links a dentry into the directory tree. @@ -1351,7 +1330,7 @@ dentry_add_child(struct wim_dentry * restrict parent, rb_link_node(&child->rb_node, rb_parent, new); rb_insert_color(&child->rb_node, root); -#ifdef __WIN32__ + /* Case insensitive child dentry index */ { struct wim_dentry *existing; existing = dentry_add_child_case_insensitive(parent, child); @@ -1363,7 +1342,6 @@ dentry_add_child(struct wim_dentry * restrict parent, INIT_LIST_HEAD(&child->case_insensitive_conflict_list); } } -#endif return NULL; } @@ -1376,7 +1354,7 @@ unlink_dentry(struct wim_dentry *dentry) if (parent == dentry) return; rb_erase(&dentry->rb_node, &parent->d_inode->i_children); -#ifdef __WIN32__ + if (dentry->rb_node_case_insensitive.__rb_parent_color) { /* This dentry was in the case-insensitive red-black tree. */ rb_erase(&dentry->rb_node_case_insensitive, @@ -1395,7 +1373,76 @@ unlink_dentry(struct wim_dentry *dentry) } } list_del(&dentry->case_insensitive_conflict_list); -#endif +} + +static int +free_dentry_full_path(struct wim_dentry *dentry, void *_ignore) +{ + FREE(dentry->_full_path); + dentry->_full_path = NULL; + return 0; +} + +/* Rename a file or directory in the WIM. */ +int +rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to, + CASE_SENSITIVITY_TYPE case_type) +{ + struct wim_dentry *src; + struct wim_dentry *dst; + struct wim_dentry *parent_of_dst; + int ret; + + /* This rename() implementation currently only supports actual files + * (not alternate data streams) */ + + src = get_dentry(wim, from, case_type); + if (!src) + return -errno; + + dst = get_dentry(wim, to, case_type); + + if (dst) { + /* Destination file exists */ + + if (src == dst) /* Same file */ + return 0; + + if (!dentry_is_directory(src)) { + /* Cannot rename non-directory to directory. */ + if (dentry_is_directory(dst)) + return -EISDIR; + } else { + /* Cannot rename directory to a non-directory or a non-empty + * directory */ + if (!dentry_is_directory(dst)) + return -ENOTDIR; + if (dentry_has_children(dst)) + return -ENOTEMPTY; + } + parent_of_dst = dst->parent; + } else { + /* Destination does not exist */ + parent_of_dst = get_parent_dentry(wim, to, case_type); + if (!parent_of_dst) + return -errno; + + if (!dentry_is_directory(parent_of_dst)) + return -ENOTDIR; + } + + ret = set_dentry_name(src, path_basename(to)); + if (ret) + return -ENOMEM; + if (dst) { + unlink_dentry(dst); + free_dentry_tree(dst, wim->lookup_table); + } + unlink_dentry(src); + dentry_add_child(parent_of_dst, src); + if (src->_full_path) + for_dentry_in_tree(src, free_dentry_full_path, NULL); + return 0; } /* @@ -1443,7 +1490,8 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, do { if (ads_entry_has_name(&inode->i_ads_entries[i], stream_name_utf16le, - stream_name_utf16le_nbytes)) + stream_name_utf16le_nbytes, + false)) { if (idx_ret) *idx_ret = i; @@ -2587,7 +2635,7 @@ image_do_iterate_dir_tree(WIMStruct *wim) struct image_iterate_dir_tree_ctx *ctx = wim->private; struct wim_dentry *dentry; - dentry = get_dentry(wim, ctx->path); + dentry = get_dentry(wim, ctx->path, WIMLIB_CASE_PLATFORM_DEFAULT); if (dentry == NULL) return WIMLIB_ERR_PATH_DOES_NOT_EXIST; return do_iterate_dir_tree(wim, dentry, ctx->flags, ctx->cb, ctx->user_ctx); @@ -2755,7 +2803,8 @@ dentry_reference_template(struct wim_dentry *dentry, void *_args) if (ret) return ret; - template_dentry = get_dentry(template_wim, dentry->_full_path); + template_dentry = get_dentry(template_wim, dentry->_full_path, + WIMLIB_CASE_SENSITIVE); if (template_dentry == NULL) { DEBUG("\"%"TS"\": newly added file", dentry->_full_path); return 0; diff --git a/src/encoding.c b/src/encoding.c index 42f7e95f..fec6e06a 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -27,6 +27,7 @@ #include "wimlib.h" #include "wimlib/encoding.h" +#include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/list.h" #include "wimlib/util.h" @@ -381,3 +382,149 @@ iconv_global_cleanup(void) iconv_cleanup(&iconv_utf8_to_utf16le); #endif } + +/* Upper case table --- Borrowed from NTFS-3g + * + * Copyright (c) 2000-2004 Anton Altaparmakov + * Copyright (c) 2002-2009 Szabolcs Szakacsits + * Copyright (c) 2008-2011 Jean-Pierre Andre + * Copyright (c) 2008 Bernhard Kaindl + * + * License is GPLv2 or later. + */ + +/** + * ntfs_upcase_table_build - build the default upcase table for NTFS + * @uc: destination buffer where to store the built table + * @uc_len: size of destination buffer in bytes + * + * ntfs_upcase_table_build() builds the default upcase table for NTFS and + * stores it in the caller supplied buffer @uc of size @uc_len. + * + * Note, @uc_len must be at least 128kiB in size or bad things will happen! + */ +static void +ntfs_upcase_table_build(utf16lechar *uc, u32 uc_len) +{ + /* + * This is the table as defined by Vista + */ + /* + * "Start" is inclusive and "End" is exclusive, every value has the + * value of "Add" added to it. + */ + static int uc_run_table[][3] = { /* Start, End, Add */ + {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32}, + {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130}, + {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32}, + {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64}, + {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80}, + {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8}, + {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8}, + {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8}, + {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74}, + {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128}, + {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8}, + {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8}, + {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8}, + {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9}, + {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48}, + {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0} + }; + /* + * "Start" is exclusive and "End" is inclusive, every second value is + * decremented by one. + */ + static int uc_dup_table[][2] = { /* Start, End */ + {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178}, + {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd}, + {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220}, + {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f}, + {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481}, + {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce}, + {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513}, + {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61}, + {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0} + }; + /* + * Set the Unicode character at offset "Offset" to "Value". Note, + * "Value" is host endian. + */ + static int uc_byte_table[][2] = { /* Offset, Value */ + {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184}, + {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6}, + {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7}, + {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc}, + {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca}, + {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66}, + {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190}, + {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196}, + {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f}, + {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae}, + {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9}, + {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0} + }; + int i, r; + int k, off; + + memset((char*)uc, 0, uc_len); + uc_len >>= 1; + if (uc_len > 65536) + uc_len = 65536; + for (i = 0; (u32)i < uc_len; i++) + uc[i] = cpu_to_le16(i); + for (r = 0; uc_run_table[r][0]; r++) { + off = uc_run_table[r][2]; + for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) + uc[i] = cpu_to_le16(i + off); + } + for (r = 0; uc_dup_table[r][0]; r++) + for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) + uc[i + 1] = cpu_to_le16(i); + for (r = 0; uc_byte_table[r][0]; r++) { + k = uc_byte_table[r][1]; + uc[uc_byte_table[r][0]] = cpu_to_le16(k); + } +} + +static utf16lechar upcase[65536]; + +void +init_upcase(void) +{ + ntfs_upcase_table_build(upcase, ARRAY_LEN(upcase)); +} + +/* Compare UTF-16LE strings case-sensitively (%ignore_case == false) or + * case-insensitively (%ignore_case == true). + * + * This is implemented using the default upper-case table used by NTFS. It does + * not handle all possible cases allowed by UTF-16LE. For example, different + * normalizations of the same sequence of "characters" are not considered equal. + * It hopefully does the right thing most of the time though. */ +int +cmp_utf16le_strings(const utf16lechar *s1, size_t n1, + const utf16lechar *s2, size_t n2, + bool ignore_case) +{ + size_t n = min(n1, n2); + + if (ignore_case) { + for (size_t i = 0; i < n; i++) { + u16 c1 = le16_to_cpu(upcase[le16_to_cpu(s1[i])]); + u16 c2 = le16_to_cpu(upcase[le16_to_cpu(s2[i])]); + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + } + } else { + for (size_t i = 0; i < n; i++) { + u16 c1 = le16_to_cpu(s1[i]); + u16 c2 = le16_to_cpu(s2[i]); + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + } + } + if (n1 == n2) + return 0; + return (n1 < n2) ? -1 : 1; +} diff --git a/src/extract.c b/src/extract.c index aba92865..4204acab 100644 --- a/src/extract.c +++ b/src/extract.c @@ -2274,7 +2274,7 @@ extract_tree(WIMStruct *wim, const tchar *wim_source_path, const tchar *target, /* Translate the path to extract into the corresponding * `struct wim_dentry', which will be the root of the * "dentry tree" to extract. */ - root = get_dentry(wim, wim_source_path); + root = get_dentry(wim, wim_source_path, WIMLIB_CASE_PLATFORM_DEFAULT); if (!root) { ERROR("Path \"%"TS"\" does not exist in WIM image %d", wim_source_path, wim->current_image); @@ -3090,7 +3090,7 @@ wimlib_extract_paths(WIMStruct *wim, else wildcard_flags |= WILDCARD_FLAG_WARN_IF_NO_MATCH; - if (extract_flags & WIMLIB_EXTRACT_FLAG_CASE_INSENSITIVE_GLOB) + if (default_ignore_case) wildcard_flags |= WILDCARD_FLAG_CASE_INSENSITIVE; ret = expand_wildcard_wim_paths(wim, paths, num_paths, diff --git a/src/lookup_table.c b/src/lookup_table.c index 357d6438..11dd2a37 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -1174,7 +1174,7 @@ wim_pathname_to_stream(WIMStruct *wim, } } - dentry = get_dentry(wim, path); + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (p) *p = T(':'); if (!dentry) diff --git a/src/lzms-compress.c b/src/lzms-compress.c index b57b86e6..af7f85b6 100644 --- a/src/lzms-compress.c +++ b/src/lzms-compress.c @@ -599,18 +599,6 @@ lzms_encode_lz_match(struct lzms_compressor *ctx, u32 length, u32 offset) lzms_end_encode_item(ctx, length); } -static struct lzms_match -lzms_get_best_match(struct lzms_compressor *ctx) -{ - struct lzms_match match; - - /* TODO */ - - match.length = 0; - - return match; -} - static void lzms_record_literal(u8 literal, void *_ctx) { @@ -652,6 +640,19 @@ lzms_fast_encode(struct lzms_compressor *ctx) } #if 0 + +static struct lzms_match +lzms_get_best_match(struct lzms_compressor *ctx) +{ + struct lzms_match match; + + /* TODO */ + + match.length = 0; + + return match; +} + static void lzms_slow_encode(struct lzms_compressor *ctx) { diff --git a/src/lzms-decompress.c b/src/lzms-decompress.c index 00e97d27..f010c95c 100644 --- a/src/lzms-decompress.c +++ b/src/lzms-decompress.c @@ -556,7 +556,6 @@ lzms_range_decode_bit(struct lzms_range_decoder *dec) static void lzms_rebuild_adaptive_huffman_code(struct lzms_huffman_decoder *dec) { - int ret; /* XXX: This implementation makes use of code already implemented for * the XPRESS and LZX compression formats. However, since for the @@ -569,9 +568,12 @@ lzms_rebuild_adaptive_huffman_code(struct lzms_huffman_decoder *dec) dec->num_syms); make_canonical_huffman_code(dec->num_syms, LZMS_MAX_CODEWORD_LEN, dec->sym_freqs, dec->lens, dec->codewords); - ret = make_huffman_decode_table(dec->decode_table, dec->num_syms, - LZMS_DECODE_TABLE_BITS, dec->lens, - LZMS_MAX_CODEWORD_LEN); +#if defined(ENABLE_LZMS_DEBUG) + int ret = +#endif + make_huffman_decode_table(dec->decode_table, dec->num_syms, + LZMS_DECODE_TABLE_BITS, dec->lens, + LZMS_MAX_CODEWORD_LEN); LZMS_ASSERT(ret == 0); } diff --git a/src/mount_image.c b/src/mount_image.c index 9a0c8a72..02a863c9 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -329,7 +329,7 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx); int ret; - parent = get_parent_dentry(wimfs_ctx->wim, path); + parent = get_parent_dentry(wimfs_ctx->wim, path, WIMLIB_CASE_SENSITIVE); if (!parent) return -errno; @@ -337,7 +337,7 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, return -ENOTDIR; basename = path_basename(path); - if (get_dentry_child_with_name(parent, basename)) + if (get_dentry_child_with_name(parent, basename, WIMLIB_CASE_SENSITIVE)) return -EEXIST; ret = new_dentry_with_inode(basename, &new); @@ -367,6 +367,17 @@ create_dentry(struct fuse_context *fuse_ctx, const char *path, return 0; } +static struct wim_inode * +wim_pathname_to_inode(WIMStruct *wim, const tchar *path) +{ + struct wim_dentry *dentry; + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); + if (dentry) + return dentry->d_inode; + else + return NULL; +} + /* Remove a dentry from a mounted WIM image; i.e. remove an alias for the * corresponding inode. * @@ -1695,14 +1706,15 @@ wimfs_link(const char *to, const char *from) FILE_ATTRIBUTE_REPARSE_POINT)) return -EPERM; - from_dentry_parent = get_parent_dentry(wim, from); + from_dentry_parent = get_parent_dentry(wim, from, WIMLIB_CASE_SENSITIVE); if (!from_dentry_parent) return -errno; if (!dentry_is_directory(from_dentry_parent)) return -ENOTDIR; link_name = path_basename(from); - if (get_dentry_child_with_name(from_dentry_parent, link_name)) + if (get_dentry_child_with_name(from_dentry_parent, link_name, + WIMLIB_CASE_SENSITIVE)) return -EEXIST; ret = new_dentry(link_name, &from_dentry); @@ -2084,7 +2096,8 @@ wimfs_removexattr(const char *path, const char *name) static int wimfs_rename(const char *from, const char *to) { - return rename_wim_path(wimfs_get_WIMStruct(), from, to); + return rename_wim_path(wimfs_get_WIMStruct(), from, to, + WIMLIB_CASE_SENSITIVE); } /* Remove a directory */ @@ -2094,7 +2107,7 @@ wimfs_rmdir(const char *path) struct wim_dentry *dentry; WIMStruct *wim = wimfs_get_WIMStruct(); - dentry = get_dentry(wim, path); + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (!dentry) return -errno; @@ -2262,7 +2275,7 @@ wimfs_utimens(const char *path, const struct timespec tv[2]) struct wim_inode *inode; WIMStruct *wim = wimfs_get_WIMStruct(); - dentry = get_dentry(wim, path); + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (!dentry) return -errno; inode = dentry->d_inode; @@ -2289,7 +2302,7 @@ wimfs_utime(const char *path, struct utimbuf *times) struct wim_inode *inode; WIMStruct *wim = wimfs_get_WIMStruct(); - dentry = get_dentry(wim, path); + dentry = get_dentry(wim, path, WIMLIB_CASE_SENSITIVE); if (!dentry) return -errno; inode = dentry->d_inode; diff --git a/src/ntfs-3g_capture.c b/src/ntfs-3g_capture.c index 03c3282d..c26ca605 100644 --- a/src/ntfs-3g_capture.c +++ b/src/ntfs-3g_capture.c @@ -183,7 +183,7 @@ capture_ntfs_streams(struct wim_inode *inode, /* Capture each data stream or reparse data stream. */ while (!ntfs_attr_lookup(type, NULL, 0, - CASE_SENSITIVE, 0, NULL, 0, actx)) + WIMLIB_CASE_SENSITIVE, 0, NULL, 0, actx)) { u64 data_size = ntfs_get_attribute_value_length(actx->attr); u64 name_length = actx->attr->name_length; @@ -535,7 +535,7 @@ build_dentry_tree_ntfs_recursive(struct wim_dentry **root_ret, le32 attributes; int ret; struct wim_dentry *root = NULL; - struct wim_inode *inode; + struct wim_inode *inode = NULL; ATTR_TYPES stream_type; if (exclude_path(path, path_len, params->config, false)) { diff --git a/src/resource.c b/src/resource.c index 192ffb2e..e71668d7 100644 --- a/src/resource.c +++ b/src/resource.c @@ -175,9 +175,9 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec, /* Get the maximum size of uncompressed chunks in this resource, which * we require be a power of 2. */ - u32 chunk_size; + u32 chunk_size = 0; u64 cur_read_offset = rspec->offset_in_wim; - int ctype; + int ctype = WIMLIB_COMPRESSION_TYPE_NONE; if (alt_chunk_table) { /* Alternate chunk table format. Its header specifies the chunk * size and compression format. */ diff --git a/src/unix_capture.c b/src/unix_capture.c index 0d9b3998..7161d363 100644 --- a/src/unix_capture.c +++ b/src/unix_capture.c @@ -203,7 +203,7 @@ unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, { struct wim_dentry *root = NULL; int ret; - struct wim_inode *inode; + struct wim_inode *inode = NULL; struct stat stbuf; if (exclude_path(path, path_len, params->config, true)) { diff --git a/src/update_image.c b/src/update_image.c index eb848e36..bd8702e2 100644 --- a/src/update_image.c +++ b/src/update_image.c @@ -98,7 +98,7 @@ do_overlay(struct wim_dentry *target, struct wim_dentry *branch) */ static int attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, - tchar *target_path) + tchar *target_path, CASE_SENSITIVITY_TYPE case_type) { tchar *slash; struct wim_dentry *dentry, *parent, *target; @@ -136,7 +136,8 @@ attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, parent = *root_p; while ((slash = tstrchr(target_path, WIM_PATH_SEPARATOR))) { *slash = T('\0'); - dentry = get_dentry_child_with_name(parent, target_path); + dentry = get_dentry_child_with_name(parent, target_path, + case_type); if (!dentry) { ret = new_filler_directory(target_path, &dentry); if (ret) @@ -156,7 +157,8 @@ attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, /* If the target path already existed, overlay the branch onto it. * Otherwise, set the branch as the target path. */ target = get_dentry_child_with_utf16le_name(parent, branch->file_name, - branch->file_name_nbytes); + branch->file_name_nbytes, + case_type); if (target) { return do_overlay(target, branch); } else { @@ -265,7 +267,8 @@ execute_add_command(WIMStruct *wim, if (ret) goto out_ntfs_umount; - ret = attach_branch(&imd->root_dentry, branch, wim_target_path); + ret = attach_branch(&imd->root_dentry, branch, wim_target_path, + WIMLIB_CASE_PLATFORM_DEFAULT); if (ret) goto out_ntfs_umount; } @@ -310,7 +313,7 @@ execute_delete_command(WIMStruct *wim, DEBUG("Deleting WIM path \"%"TS"\" (flags=%#x)", wim_path, flags); - tree = get_dentry(wim, wim_path); + tree = get_dentry(wim, wim_path, WIMLIB_CASE_PLATFORM_DEFAULT); if (!tree) { /* Path to delete does not exist in the WIM. */ if (flags & WIMLIB_DELETE_FLAG_FORCE) { @@ -337,80 +340,6 @@ execute_delete_command(WIMStruct *wim, return 0; } -static int -free_dentry_full_path(struct wim_dentry *dentry, void *_ignore) -{ - FREE(dentry->_full_path); - dentry->_full_path = NULL; - return 0; -} - -/* - * Rename a file or directory in the WIM. - * - * This is also called from wimfs_rename() in the FUSE mount code. - */ -int -rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to) -{ - struct wim_dentry *src; - struct wim_dentry *dst; - struct wim_dentry *parent_of_dst; - int ret; - - /* This rename() implementation currently only supports actual files - * (not alternate data streams) */ - - src = get_dentry(wim, from); - if (!src) - return -errno; - - dst = get_dentry(wim, to); - - if (dst) { - /* Destination file exists */ - - if (src == dst) /* Same file */ - return 0; - - if (!dentry_is_directory(src)) { - /* Cannot rename non-directory to directory. */ - if (dentry_is_directory(dst)) - return -EISDIR; - } else { - /* Cannot rename directory to a non-directory or a non-empty - * directory */ - if (!dentry_is_directory(dst)) - return -ENOTDIR; - if (dentry_has_children(dst)) - return -ENOTEMPTY; - } - parent_of_dst = dst->parent; - } else { - /* Destination does not exist */ - parent_of_dst = get_parent_dentry(wim, to); - if (!parent_of_dst) - return -errno; - - if (!dentry_is_directory(parent_of_dst)) - return -ENOTDIR; - } - - ret = set_dentry_name(src, path_basename(to)); - if (ret) - return -ENOMEM; - if (dst) { - unlink_dentry(dst); - free_dentry_tree(dst, wim->lookup_table); - } - unlink_dentry(src); - dentry_add_child(parent_of_dst, src); - if (src->_full_path) - for_dentry_in_tree(src, free_dentry_full_path, NULL); - return 0; -} - - static int execute_rename_command(WIMStruct *wim, const struct wimlib_update_command *rename_cmd) @@ -420,7 +349,8 @@ execute_rename_command(WIMStruct *wim, wimlib_assert(rename_cmd->op == WIMLIB_UPDATE_OP_RENAME); ret = rename_wim_path(wim, rename_cmd->rename.wim_source_path, - rename_cmd->rename.wim_target_path); + rename_cmd->rename.wim_target_path, + WIMLIB_CASE_PLATFORM_DEFAULT); if (ret) { ret = -ret; errno = ret; diff --git a/src/wildcard.c b/src/wildcard.c index 9c878e21..06d64748 100644 --- a/src/wildcard.c +++ b/src/wildcard.c @@ -46,27 +46,73 @@ struct match_dentry_ctx { bool case_insensitive; }; +#ifdef __WIN32__ +static bool +match_wildcard_case_sensitive(const tchar *string, size_t string_len, + const tchar *wildcard, size_t wildcard_len) +{ + for (;;) { + if (string_len == 0) { + while (wildcard_len != 0 && *wildcard == T('*')) { + wildcard++; + wildcard_len--; + } + return (wildcard_len == 0); + } else if (wildcard_len == 0) { + return false; + } else if (*string == *wildcard || *wildcard == '?') { + string++; + string_len--; + wildcard_len--; + wildcard++; + continue; + } else if (*wildcard == '*') { + return match_wildcard_case_sensitive( + string, string_len, + wildcard + 1, wildcard_len - 1) || + match_wildcard_case_sensitive( + string + 1, string_len - 1, + wildcard, wildcard_len); + } else { + return false; + } + } +} +#endif + static bool match_wildcard(const tchar *string, tchar *wildcard, size_t wildcard_len, bool case_insensitive) { - char orig; - int flags; - int ret; + /* Note: in Windows builds fnmatch() calls a replacement function. + * It does support case-sensitive globbing. */ +#ifdef __WIN32__ + if (case_insensitive) +#endif + { + char orig; + int ret; + int flags = FNM_NOESCAPE; + if (case_insensitive) + flags |= FNM_CASEFOLD; - orig = wildcard[wildcard_len]; - wildcard[wildcard_len] = T('\0'); + orig = wildcard[wildcard_len]; + wildcard[wildcard_len] = T('\0'); - /* Warning: in Windows builds fnmatch() calls a replacement function. - * Also, FNM_CASEFOLD is a GNU extension and it is defined to 0 if not - * available. */ - flags = FNM_NOESCAPE; - if (case_insensitive) - flags |= FNM_CASEFOLD; - ret = fnmatch(wildcard, string, flags); + ret = fnmatch(wildcard, string, flags); - wildcard[wildcard_len] = orig; - return (ret == 0); + wildcard[wildcard_len] = orig; + return (ret == 0); + } +#ifdef __WIN32__ + else + { + return match_wildcard_case_sensitive(string, + tstrlen(string), + wildcard, + wildcard_len); + } +#endif } static int @@ -311,7 +357,7 @@ append_path_cb(const tchar *path, void *_ctx, bool may_need_trans) int expand_wildcard_wim_paths(WIMStruct *wim, - const char * const *wildcards, + const tchar * const *wildcards, size_t num_wildcards, tchar ***expanded_paths_ret, size_t *num_expanded_paths_ret, diff --git a/src/wim.c b/src/wim.c index bad95d19..6c69de35 100644 --- a/src/wim.c +++ b/src/wim.c @@ -1018,7 +1018,6 @@ WIMLIBAPI int wimlib_global_init(int init_flags) { static bool already_inited = false; - int ret; if (already_inited) return 0; @@ -1031,14 +1030,19 @@ wimlib_global_init(int init_flags) #endif } #ifdef __WIN32__ - ret = win32_global_init(init_flags); - if (ret) - return ret; -#else - ret = 0; + { + int ret = win32_global_init(init_flags); + if (ret) + return ret; + } #endif + init_upcase(); + if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_SENSITIVE) + default_ignore_case = false; + else if (init_flags & WIMLIB_INIT_FLAG_DEFAULT_CASE_INSENSITIVE) + default_ignore_case = true; already_inited = true; - return ret; + return 0; } /* API function documented in wimlib.h */ diff --git a/src/win32_capture.c b/src/win32_capture.c index fa3f04fb..8004f417 100644 --- a/src/win32_capture.c +++ b/src/win32_capture.c @@ -1123,7 +1123,7 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, unsigned vol_flags) { struct wim_dentry *root = NULL; - struct wim_inode *inode; + struct wim_inode *inode = NULL; DWORD err; u64 file_size; int ret; diff --git a/src/win32_replacements.c b/src/win32_replacements.c index 610afbdf..27d074e4 100644 --- a/src/win32_replacements.c +++ b/src/win32_replacements.c @@ -37,6 +37,7 @@ #include "wimlib/file_io.h" #include "wimlib/glob.h" #include "wimlib/error.h" +#include "wimlib/wildcard.h" #include "wimlib/util.h" /* Replacement for POSIX fsync() */ -- 2.43.0