Allow configurable case sensitivity
authorEric Biggers <ebiggers3@gmail.com>
Sat, 28 Dec 2013 04:43:24 +0000 (22:43 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 28 Dec 2013 05:00:40 +0000 (23:00 -0600)
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().

25 files changed:
doc/imagex-extract.1.in
doc/imagex.1.in
include/wimlib.h
include/wimlib/dentry.h
include/wimlib/encoding.h
include/wimlib/wildcard.h
include/wimlib/wim.h
include/wimlib/win32.h
include/wimlib_tchar.h
programs/imagex.c
src/dentry.c
src/encoding.c
src/extract.c
src/lookup_table.c
src/lzms-compress.c
src/lzms-decompress.c
src/mount_image.c
src/ntfs-3g_capture.c
src/resource.c
src/unix_capture.c
src/update_image.c
src/wildcard.c
src/wim.c
src/win32_capture.c
src/win32_replacements.c

index 6d21cee..2b9afc1 100644 (file)
@@ -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.
index 2950875..7f13df5 100644 (file)
@@ -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
index 512c614..f627044 100644 (file)
@@ -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
  * @{ */
index f514028..22f0aff 100644 (file)
@@ -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);
index a621581..59914ae 100644 (file)
@@ -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 */
index 764f478..4506ac3 100644 (file)
@@ -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 <fnmatch.h>
 #  ifndef FNM_CASEFOLD
index f045c88..3fdc7d2 100644 (file)
@@ -134,9 +134,6 @@ 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);
 
 extern int
index 6bb5b34..0501743 100644 (file)
@@ -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);
 
index 5b92310..33ad0cc 100644 (file)
@@ -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
index ce5ac8d..aa628b3 100644 (file)
@@ -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) &&
index 53a44cf..ad78f7d 100644 (file)
@@ -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;
index 42f7e95..fec6e06 100644 (file)
@@ -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;
+}
index aba9286..4204aca 100644 (file)
@@ -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,
index 357d643..11dd2a3 100644 (file)
@@ -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)
index b57b86e..af7f85b 100644 (file)
@@ -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)
 {
index 00e97d2..f010c95 100644 (file)
@@ -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);
 }
 
index 9a0c8a7..02a863c 100644 (file)
@@ -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;
index 03c3282..c26ca60 100644 (file)
@@ -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)) {
index 192ffb2..e71668d 100644 (file)
@@ -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.  */
index 0d9b399..7161d36 100644 (file)
@@ -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)) {
index eb848e3..bd8702e 100644 (file)
@@ -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) {
@@ -338,80 +341,6 @@ execute_delete_command(WIMStruct *wim,
 }
 
 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;
index 9c878e2..06d6474 100644 (file)
@@ -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,
index bad95d1..6c69de3 100644 (file)
--- 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  */
index fa3f04f..8004f41 100644 (file)
@@ -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;
index 610afbd..27d074e 100644 (file)
@@ -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() */