Avoid the need to allocate 128K of storage for the upcase table, which
makes it easier to use libntfs-3g with very little memory available.

Add a new struct, ntfs_upcase, containing the information needed to load
the upcase table on demand.  Modify all functions taking the pointer and
length for the upcase table to take an ntfs_upcase pointer instead.
Modify ntfs_upcase_table_build to support building only part of an
upcase table.  Add new function ntfs_upcase_lookup to perform an upcase
table lookup.
---
 include/ntfs-3g/unistr.h |   35 ++++++++++++---
 include/ntfs-3g/volume.h |   11 +++--
 libntfs-3g/attrib.c      |   27 +++++-------
 libntfs-3g/collate.c     |    4 +-
 libntfs-3g/dir.c         |   10 ++--
 libntfs-3g/inode.c       |    2 +-
 libntfs-3g/unistr.c      |  106 ++++++++++++++++++++++++++-------------------
 libntfs-3g/volume.c      |   61 ++++++++------------------
 8 files changed, 133 insertions(+), 123 deletions(-)

This patch applies to ntfs-3g-0.20061031-BETA.

diff --git a/include/ntfs-3g/unistr.h b/include/ntfs-3g/unistr.h
index b45101e..b47c112 100644
--- a/include/ntfs-3g/unistr.h
+++ b/include/ntfs-3g/unistr.h
@@ -25,45 +25,66 @@
 
 #include "types.h"
 #include "layout.h"
+#include "attrib.h"
+
+typedef struct _ntfs_upcase {
+       ntfschar *upcase;       /* Upper case equivalents of the Unicode
+                                  characters from upcase_offset to
+                                  upcase_offset+upcase_len-1. Obtained from
+                                  $UpCase. */
+       u32 len;                /* Length in Unicode characters of the
+                                  currently loaded portion of the upcase
+                                  table. */
+       u32 total_len;          /* Length in Unicode characters of the on-disk
+                                  upcase table, or 65536 if attr is NULL. */
+       u32 offset;             /* Offset in Unicode characters of the
+                                  currently loaded portion of the upcase table
+                                  from Unicode character 0. */
+       ntfs_attr *attr;        /* ntfs_attr for the data of the upcase file.
+                                  If NULL, build them with
+                                  ntfs_upcase_table_build. */
+} ntfs_upcase;
 
 extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
                const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic,
-               const ntfschar *upcase, const u32 upcase_size);
+               ntfs_upcase *upcase);
 
 extern int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
                const ntfschar *name2, const u32 name2_len,
                const int err_val, const IGNORE_CASE_BOOL ic,
-               const ntfschar *upcase, const u32 upcase_len);
+               ntfs_upcase *upcase);
 
 extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n);
 
 extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
-               const ntfschar *upcase, const u32 upcase_size);
+               ntfs_upcase *upcase);
 
 extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen);
 
 extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen);
 
 extern void ntfs_name_upcase(ntfschar *name, u32 name_len,
-               const ntfschar *upcase, const u32 upcase_len);
+               ntfs_upcase *upcase);
 
 extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
-               const ntfschar *upcase, const u32 upcase_len);
+               ntfs_upcase *upcase);
 
 extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
                const FILE_NAME_ATTR *file_name_attr2,
                const int err_val, const IGNORE_CASE_BOOL ic,
-               const ntfschar *upcase, const u32 upcase_len);
+               ntfs_upcase *upcase);
 
 extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
                int outs_len);
 extern int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len);
 
-extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
+extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len, u32 uc_offset);
 
 extern ntfschar *ntfs_str2ucs(const char *s, int *len);
 
 extern void ntfs_ucsfree(ntfschar *ucs);
 
+extern ntfschar ntfs_upcase_lookup(ntfs_upcase *upcase, ntfschar c);
+
 #endif /* defined _NTFS_UNISTR_H */
 
diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h
index 7f14ab9..6044027 100644
--- a/include/ntfs-3g/volume.h
+++ b/include/ntfs-3g/volume.h
@@ -61,6 +61,9 @@
 #endif
 #endif
 
+/* Upcase table uses 64K if not set, 512 bytes if set. */
+#define MS_NTFS_SMALL_UPCASE_TABLE 1073741824 /* 2**30 */
+
 /* Forward declaration */
 typedef struct _ntfs_volume ntfs_volume;
 
@@ -69,6 +72,7 @@ typedef struct _ntfs_volume ntfs_volume;
 #include "device.h"
 #include "inode.h"
 #include "attrib.h"
+#include "unistr.h"
 
 /**
  * enum ntfs_mount_flags -
@@ -195,11 +199,8 @@ struct _ntfs_volume {
        ntfs_attr *mftmirr_na;  /* ntfs_attr structure for the data attribute
                                   of FILE_MFTMirr. */
 
-       ntfschar *upcase;       /* Upper case equivalents of all 65536 2-byte
-                                  Unicode characters. Obtained from
-                                  FILE_UpCase. */
-       u32 upcase_len;         /* Length in Unicode characters of the upcase
-                                  table. */
+       ntfs_upcase upcase;     /* ntfs_upcase table to use for
+                                  case-insensitive comparisons. */
 
        ATTR_DEF *attrdef;      /* Attribute definitions. Obtained from
                                   FILE_AttrDef. */
diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c
index e35f220..ed0fa04 100644
--- a/libntfs-3g/attrib.c
+++ b/libntfs-3g/attrib.c
@@ -1542,15 +1542,13 @@ static int ntfs_attr_find(const ATTR_TYPES type, const 
ntfschar *name,
 {
        ATTR_RECORD *a;
        ntfs_volume *vol;
-       ntfschar *upcase;
-       u32 upcase_len;
+       ntfs_upcase *upcase;
 
        ntfs_log_trace("attribute type 0x%x.\n", type);
 
        if (ctx->ntfs_ino) {
                vol = ctx->ntfs_ino->vol;
-               upcase = vol->upcase;
-               upcase_len = vol->upcase_len;
+               upcase = &vol->upcase;
        } else {
                if (name && name != AT_UNNAMED) {
                        errno = EINVAL;
@@ -1558,7 +1556,6 @@ static int ntfs_attr_find(const ATTR_TYPES type, const 
ntfschar *name,
                }
                vol = NULL;
                upcase = NULL;
-               upcase_len = 0;
        }
        /*
         * Iterate over attributes in mft record starting at @ctx->attr, or the
@@ -1601,14 +1598,14 @@ static int ntfs_attr_find(const ATTR_TYPES type, const 
ntfschar *name,
                        }
                } else if (name && !ntfs_names_are_equal(name, name_len,
                            (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)),
-                           a->name_length, ic, upcase, upcase_len)) {
+                           a->name_length, ic, upcase)) {
                        register int rc;
 
                        rc = ntfs_names_collate(name, name_len,
                                        (ntfschar*)((char*)a +
                                        le16_to_cpu(a->name_offset)),
                                        a->name_length, 1, IGNORE_CASE,
-                                       upcase, upcase_len);
+                                       upcase);
                        /*
                         * If @name collates before a->name, there is no
                         * matching attribute.
@@ -1624,7 +1621,7 @@ static int ntfs_attr_find(const ATTR_TYPES type, const 
ntfschar *name,
                                        (ntfschar*)((char*)a +
                                        le16_to_cpu(a->name_offset)),
                                        a->name_length, 1, CASE_SENSITIVE,
-                                       upcase, upcase_len);
+                                       upcase);
                        if (rc == -1) {
                                errno = ENOENT;
                                return -1;
@@ -1909,13 +1906,12 @@ find_attr_list_attr:
                        if (al_name_len)
                                goto not_found;
                } else if (name && !ntfs_names_are_equal(al_name, al_name_len,
-                               name, name_len, ic, vol->upcase,
-                               vol->upcase_len)) {
+                               name, name_len, ic, &vol->upcase)) {
                        register int rc;
 
                        rc = ntfs_names_collate(name, name_len, al_name,
                                        al_name_len, 1, IGNORE_CASE,
-                                       vol->upcase, vol->upcase_len);
+                                       &vol->upcase);
                        /*
                         * If @name collates before al_name, there is no
                         * matching attribute.
@@ -1935,7 +1931,7 @@ find_attr_list_attr:
                         */
                        rc = ntfs_names_collate(name, name_len, al_name,
                                        al_name_len, 1, CASE_SENSITIVE,
-                                       vol->upcase, vol->upcase_len);
+                                       &vol->upcase);
                        if (rc == -1)
                                goto not_found;
                        if (rc)
@@ -1960,7 +1956,7 @@ find_attr_list_attr:
                                        next_al_entry->name_offset),
                                        next_al_entry->name_length,
                                        al_name, al_name_len, CASE_SENSITIVE,
-                                       vol->upcase, vol->upcase_len))
+                                       &vol->upcase))
                        continue;
 is_enumeration:
                if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {
@@ -2029,7 +2025,7 @@ do_next_attr_loop:
                                le16_to_cpu(a->name_offset)),
                                a->name_length, al_name,
                                al_name_len, CASE_SENSITIVE,
-                               vol->upcase, vol->upcase_len))
+                               &vol->upcase))
                        break;
                ctx->attr = a;
                /*
@@ -2182,8 +2178,7 @@ int ntfs_attr_lookup(const ATTR_TYPES type, const 
ntfschar *name,
        ntfs_inode *base_ni;
 
        if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED &&
-                       (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) ||
-                       !vol->upcase || !vol->upcase_len))) {
+                       (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol)))) {
                errno = EINVAL;
                return -1;
        }
diff --git a/libntfs-3g/collate.c b/libntfs-3g/collate.c
index 2352423..c76acb7 100644
--- a/libntfs-3g/collate.c
+++ b/libntfs-3g/collate.c
@@ -141,11 +141,11 @@ static int ntfs_collate_file_name(ntfs_volume *vol,
 
        ntfs_log_trace("Entering.\n");
        rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR,
-                       IGNORE_CASE, vol->upcase, vol->upcase_len);
+                       IGNORE_CASE, &vol->upcase);
        if (!rc)
                rc = ntfs_file_values_compare(data1, data2,
                                NTFS_COLLATION_ERROR, CASE_SENSITIVE,
-                               vol->upcase, vol->upcase_len);
+                               &vol->upcase);
        ntfs_log_trace("Done, returning %i.\n", rc);
        return rc;
 }
diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c
index 328a7bf..e529410 100644
--- a/libntfs-3g/dir.c
+++ b/libntfs-3g/dir.c
@@ -175,7 +175,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const 
ntfschar *uname,
                rc = ntfs_names_collate(uname, uname_len,
                                (ntfschar*)&ie->key.file_name.file_name,
                                ie->key.file_name.file_name_length, 1,
-                               IGNORE_CASE, vol->upcase, vol->upcase_len);
+                               IGNORE_CASE, &vol->upcase);
                /*
                 * If uname collates before the name of the current entry, there
                 * is definitely no such name in this index but we might need to
@@ -194,7 +194,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const 
ntfschar *uname,
                rc = ntfs_names_collate(uname, uname_len,
                                (ntfschar*)&ie->key.file_name.file_name,
                                ie->key.file_name.file_name_length, 1,
-                               CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+                               CASE_SENSITIVE, &vol->upcase);
                if (rc == -1)
                        break;
                if (rc)
@@ -323,7 +323,7 @@ descend_into_child_node:
                rc = ntfs_names_collate(uname, uname_len,
                                (ntfschar*)&ie->key.file_name.file_name,
                                ie->key.file_name.file_name_length, 1,
-                               IGNORE_CASE, vol->upcase, vol->upcase_len);
+                               IGNORE_CASE, &vol->upcase);
                /*
                 * If uname collates before the name of the current entry, there
                 * is definitely no such name in this index but we might need to
@@ -342,7 +342,7 @@ descend_into_child_node:
                rc = ntfs_names_collate(uname, uname_len,
                                (ntfschar*)&ie->key.file_name.file_name,
                                ie->key.file_name.file_name_length, 1,
-                               CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+                               CASE_SENSITIVE, &vol->upcase);
                if (rc == -1)
                        break;
                if (rc)
@@ -1437,7 +1437,7 @@ search:
                
                if (ntfs_names_are_equal(fn->file_name, fn->file_name_length,
                                         name, name_len, case_sensitive, 
-                                        ni->vol->upcase, ni->vol->upcase_len)){
+                                        &ni->vol->upcase)){
                        
                        if (fn->file_name_type == FILE_NAME_WIN32) {
                                looking_for_dos_name = TRUE;
diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c
index 7e22f34..5a5e561 100644
--- a/libntfs-3g/inode.c
+++ b/libntfs-3g/inode.c
@@ -1148,7 +1148,7 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr)
 
        if (ustr && ntfs_names_are_equal(ustr, len,
                        (ntfschar *)((u8 *)attr + 
le16_to_cpu(attr->name_offset)),
-                       attr->name_length, 0, NULL, 0))
+                       attr->name_length, 0, NULL))
                ret = 1;
 
        ntfs_ucsfree(ustr);
diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c
index 4eb9dee..b04d2fb 100644
--- a/libntfs-3g/unistr.c
+++ b/libntfs-3g/unistr.c
@@ -83,7 +83,6 @@ static const u8 legal_ansi_char_array[0x40] = {
  * @s2_len:            length in Unicode characters of @s2
  * @ic:                        ignore case bool
  * @upcase:            upcase table (only if @ic == IGNORE_CASE)
- * @upcase_size:       length in Unicode characters of @upcase (if present)
  *
  * Compare the names @s1 and @s2 and return TRUE (1) if the names are
  * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE,
@@ -91,8 +90,7 @@ static const u8 legal_ansi_char_array[0x40] = {
  */
 BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
                const ntfschar *s2, size_t s2_len,
-               const IGNORE_CASE_BOOL ic,
-               const ntfschar *upcase, const u32 upcase_size)
+               const IGNORE_CASE_BOOL ic, ntfs_upcase *upcase)
 {
        if (s1_len != s2_len)
                return FALSE;
@@ -100,8 +98,7 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
                return TRUE;
        if (ic == CASE_SENSITIVE)
                return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE;
-       return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE:
-                                                                      TRUE;
+       return ntfs_ucsncasecmp(s1, s2, s1_len, upcase) ? FALSE: TRUE;
 }
 
 /**
@@ -113,7 +110,6 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
  * @err_val:   if @name1 contains an invalid character return this value
  * @ic:                either CASE_SENSITIVE or IGNORE_CASE
  * @upcase:    upcase table (ignored if @ic is CASE_SENSITIVE)
- * @upcase_len:        upcase table size (ignored if @ic is CASE_SENSITIVE)
  *
  * ntfs_names_collate() collates two Unicode names and returns:
  *
@@ -127,14 +123,13 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t 
s1_len,
 int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
                const ntfschar *name2, const u32 name2_len,
                const int err_val __attribute__((unused)),
-               const IGNORE_CASE_BOOL ic, const ntfschar *upcase,
-               const u32 upcase_len)
+               const IGNORE_CASE_BOOL ic, ntfs_upcase *upcase)
 {
        u32 cnt;
        ntfschar c1, c2;
 
 #ifdef DEBUG
-       if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) {
+       if (!name1 || !name2 || (ic && !upcase)) {
                ntfs_log_debug("ntfs_names_collate received NULL pointer!\n");
                exit(1);
        }
@@ -145,10 +140,8 @@ int ntfs_names_collate(const ntfschar *name1, const u32 
name1_len,
                c2 = le16_to_cpu(*name2);
                name2++;
                if (ic) {
-                       if (c1 < upcase_len)
-                               c1 = le16_to_cpu(upcase[c1]);
-                       if (c2 < upcase_len)
-                               c2 = le16_to_cpu(upcase[c2]);
+                       c1 = le16_to_cpu(ntfs_upcase_lookup(upcase, c1));
+                       c2 = le16_to_cpu(ntfs_upcase_lookup(upcase, c2));
                }
 #if 0
                if (c1 < 64 && legal_ansi_char_array[c1] & 8)
@@ -216,7 +209,6 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, 
size_t n)
  * @s2:                        second string
  * @n:                 maximum unicode characters to compare
  * @upcase:            upcase table
- * @upcase_size:       upcase table size in Unicode characters
  *
  * Compare the first @n characters of the Unicode strings @s1 and @s2,
  * ignoring case. The strings in little endian format and appropriate
@@ -229,7 +221,7 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, 
size_t n)
  * to be less than, to match, or be greater than @s2.
  */
 int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
-               const ntfschar *upcase, const u32 upcase_size)
+               ntfs_upcase *upcase)
 {
        ntfschar c1, c2;
        size_t i;
@@ -241,10 +233,8 @@ int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar 
*s2, size_t n,
        }
 #endif
        for (i = 0; i < n; ++i) {
-               if ((c1 = le16_to_cpu(s1[i])) < upcase_size)
-                       c1 = le16_to_cpu(upcase[c1]);
-               if ((c2 = le16_to_cpu(s2[i])) < upcase_size)
-                       c2 = le16_to_cpu(upcase[c2]);
+               c1 = le16_to_cpu(ntfs_upcase_lookup(upcase, 
le16_to_cpu(s1[i])));
+               c2 = le16_to_cpu(ntfs_upcase_lookup(upcase, 
le16_to_cpu(s2[i])));
                if (c1 < c2)
                        return -1;
                if (c1 > c2)
@@ -314,38 +304,31 @@ ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen)
  * @name:
  * @name_len:
  * @upcase:
- * @upcase_len:
  *
  * Description...
  *
  * Returns:
  */
-void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase,
-               const u32 upcase_len)
+void ntfs_name_upcase(ntfschar *name, u32 name_len, ntfs_upcase *upcase)
 {
        u32 i;
-       ntfschar u;
-
        for (i = 0; i < name_len; i++)
-               if ((u = le16_to_cpu(name[i])) < upcase_len)
-                       name[i] = upcase[u];
+               name[i] = ntfs_upcase_lookup(upcase, le16_to_cpu(name[i]));
 }
 
 /**
  * ntfs_file_value_upcase - Convert a filename to upper case
  * @file_name_attr:
  * @upcase:
- * @upcase_len:
  *
  * Description...
  *
  * Returns:
  */
-void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
-               const ntfschar *upcase, const u32 upcase_len)
+void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, ntfs_upcase 
*upcase)
 {
        ntfs_name_upcase((ntfschar*)&file_name_attr->file_name,
-                       file_name_attr->file_name_length, upcase, upcase_len);
+                       file_name_attr->file_name_length, upcase);
 }
 
 /**
@@ -355,7 +338,6 @@ void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
  * @err_val:
  * @ic:
  * @upcase:
- * @upcase_len:
  *
  * Description...
  *
@@ -364,13 +346,13 @@ void ntfs_file_value_upcase(FILE_NAME_ATTR 
*file_name_attr,
 int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
                const FILE_NAME_ATTR *file_name_attr2,
                const int err_val, const IGNORE_CASE_BOOL ic,
-               const ntfschar *upcase, const u32 upcase_len)
+               ntfs_upcase *upcase)
 {
        return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name,
                        file_name_attr1->file_name_length,
                        (ntfschar*)&file_name_attr2->file_name,
                        file_name_attr2->file_name_length,
-                       err_val, ic, upcase, upcase_len);
+                       err_val, ic, upcase);
 }
 
 /**
@@ -642,13 +624,13 @@ err_out:
  * 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
+ * @uc_offset: desired offset of the start of the table in Unicode characters
  *
- * 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!
+ * ntfs_upcase_table_build() builds the default upcase table for NTFS, starting
+ * from Unicode character uc_offset, and stores it in the caller supplied
+ * buffer @uc of size @uc_len.
  */
-void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
+void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len, u32 uc_offset)
 {
        static int uc_run_table[][3] = { /* Start, End, Add */
        {0x0061, 0x007B,  -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72,  74},
@@ -688,20 +670,20 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
        };
        int i, r;
 
+#define uc_set(i, v) do { if (i > uc_offset && i < uc_len + uc_offset) uc[i - 
uc_offset] = v; } while(0)
        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] = i;
+       for (i = uc_offset; (u32)i < uc_offset + uc_len; i++)
+               uc_set(i, i);
        for (r = 0; uc_run_table[r][0]; r++)
                for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++)
-                       uc[i] += uc_run_table[r][2];
+                       uc_set(i, i + uc_run_table[r][2]);
        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]--;
+                       uc_set(i + 1, uc[i + 1 - uc_offset] - 1);
        for (r = 0; uc_byte_table[r][0]; r++)
-               uc[uc_byte_table[r][0]] = uc_byte_table[r][1];
+               uc_set(uc_byte_table[r][0], uc_byte_table[r][1]);
+#undef uc_set
 }
 
 /**
@@ -755,3 +737,37 @@ void ntfs_ucsfree(ntfschar *ucs)
                free(ucs);
 }
 
+/**
+ * ntfs_upcase_lookup - upcase a character via an upcase table
+ * @upcase     upcase table
+ * @c          character to upcase
+ *
+ * upcase the character @c via the upcase table @upcase.
+ *
+ * Return value: upcased character.
+ */
+ntfschar ntfs_upcase_lookup(ntfs_upcase *upcase, ntfschar c)
+{
+       if(c >= upcase->total_len)
+               return cpu_to_le16(c);
+
+       if(!(upcase->offset <= c && c < upcase->offset + upcase->len)) {
+               upcase->offset = (c + upcase->len - 1) & (upcase->len - 1);
+               if(upcase->attr) {
+                       u32 len = upcase->len;
+                       if(upcase->offset + len > upcase->total_len)
+                               len = upcase->total_len - upcase->offset;
+                       if(ntfs_attr_pread(upcase->attr, upcase->offset << 1,
+                                          len << 1, upcase->upcase) != (len << 
1)) {
+                               ntfs_log_perror("Failed to read upcase file; 
not upcasing character.");
+                               upcase->offset = 65536;
+                               return cpu_to_le16(c);
+                       }
+               } else {
+                       ntfs_upcase_table_build(upcase->upcase, upcase->len,
+                                               upcase->offset);
+               }
+       }
+
+       return upcase->upcase[c - upcase->offset];
+}
diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c
index 7088ba8..e1945ba 100644
--- a/libntfs-3g/volume.c
+++ b/libntfs-3g/volume.c
@@ -120,7 +120,11 @@ static void __ntfs_volume_release(ntfs_volume *v)
                        ntfs_log_perror("Eeek! Failed to close the device.  
Error: ");
        }
        free(v->vol_name);
-       free(v->upcase);
+       if (v->upcase.attr) {
+               ntfs_attr_close(v->upcase.attr);
+               ntfs_inode_close(v->upcase.attr->ni);
+       }
+       free(v->upcase.upcase);
        free(v->attrdef);
        free(v);
 }
@@ -417,14 +421,16 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, 
unsigned long flags)
        vol = ntfs_volume_alloc();
        if (!vol)
                goto error_exit;
+
        /* Create the default upcase table. */
-       vol->upcase_len = 65536;
-       vol->upcase = ntfs_malloc(vol->upcase_len * sizeof(ntfschar));
-       if (!vol->upcase)
+       vol->upcase.len = (flags & MS_NTFS_SMALL_UPCASE_TABLE) ? 256 : 65536;
+       vol->upcase.total_len = 65536;
+       vol->upcase.offset = 65536; /* Force the first lookup to fail. */
+       vol->upcase.attr = NULL;
+       vol->upcase.upcase = ntfs_malloc(vol->upcase.len * sizeof(ntfschar));
+       if (!vol->upcase.upcase)
                goto error_exit;
        
-       ntfs_upcase_table_build(vol->upcase,
-                       vol->upcase_len * sizeof(ntfschar));
        if (flags & MS_RDONLY)
                NVolSetReadOnly(vol);
        if (flags & MS_NOATIME)
@@ -903,43 +909,14 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, 
unsigned long flags)
                ntfs_log_perror("Failed to open ntfs attribute");
                goto error_exit;
        }
-       /*
-        * Note: Normally, the upcase table has a length equal to 65536
-        * 2-byte Unicode characters but allow for different cases, so no
-        * checks done. Just check we don't overflow 32-bits worth of Unicode
-        * characters.
-        */
-       if (na->data_size & ~0x1ffffffffULL) {
-               ntfs_log_debug(FAILED);
-               ntfs_log_debug("Error: Upcase table is too big (max 32-bit "
-                               "allowed).\n");
-               errno = EINVAL;
-               goto error_exit;
-       }
-       if (vol->upcase_len != na->data_size >> 1) {
-               vol->upcase_len = na->data_size >> 1;
-               /* Throw away default table. */
-               free(vol->upcase);
-               vol->upcase = ntfs_malloc(na->data_size);
-               if (!vol->upcase) {
-                       ntfs_log_debug(FAILED);
-                       goto error_exit;
-               }
-       }
-       /* Read in the $DATA attribute value into the buffer. */
-       l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase);
-       if (l != na->data_size) {
-               ntfs_log_debug(FAILED);
-               ntfs_log_debug("Amount of data read does not correspond to 
expected "
-                               "length!\n");
-               errno = EIO;
-               goto error_exit;
-       }
-       /* Done with the $UpCase mft record. */
+       /* Save off the data attribute and length for upcase table lookups. */
+       vol->upcase.attr = na;
+       vol->upcase.total_len = na->data_size >> 1;
+       if(vol->upcase.total_len > 65536)
+               vol->upcase.total_len = 65536;
+       /* Don't keep using the currently-loaded upcase table. */
+       vol->upcase.offset = 65536;
        ntfs_log_debug(OK);
-       ntfs_attr_close(na);
-       if (ntfs_inode_close(ni))
-               ntfs_log_perror("Failed to close inode, leaking memory");
 
        /*
         * Now load $Volume and set the version information and flags in the
-- 
1.4.4.2

Attachment: signature.asc
Description: Digital signature

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
ntfs-3g-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ntfs-3g-devel

Reply via email to