enabled case insensitive file names in lowntfs-3g

PERMISSION_HANDLING_BRANCH
Jean-Pierre André 2010-05-25 10:12:44 +02:00
parent 81f1de0559
commit 693aa8780d
7 changed files with 256 additions and 53 deletions

View File

@ -47,6 +47,9 @@ 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);
extern void ntfs_name_locase(ntfschar *name, u32 name_len,
const ntfschar *locase, const u32 locase_len);
extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
const ntfschar *upcase, const u32 upcase_len);
@ -59,7 +62,11 @@ 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);
extern char *ntfs_uppercase_mbs(const char *low,
const ntfschar *upcase, u32 upcase_len);
extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt);
extern ntfschar *ntfs_str2ucs(const char *s, int *len);

View File

@ -232,6 +232,9 @@ struct _ntfs_volume {
FILE_UpCase. */
u32 upcase_len; /* Length in Unicode characters of the upcase
table. */
ntfschar *locase; /* Lower case equivalents of all 65536 2-byte
Unicode characters. Only if option
case_ignore is set. */
ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
FILE_AttrDef. */
@ -289,6 +292,7 @@ extern int ntfs_volume_get_free_space(ntfs_volume *vol);
extern int ntfs_set_shown_files(ntfs_volume *vol,
BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);
extern int ntfs_set_locale(void);
extern int ntfs_set_ignore_case(ntfs_volume *vol);
#endif /* defined _NTFS_VOLUME_H */

View File

@ -253,6 +253,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
INDEX_ROOT *ir;
INDEX_ENTRY *ie;
INDEX_ALLOCATION *ia;
IGNORE_CASE_BOOL case_sensitivity;
u8 *index_end;
ntfs_attr *ia_na;
int eo, rc;
@ -277,6 +278,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
"%lld", (unsigned long long)dir_ni->mft_no);
goto put_err_out;
}
case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE);
/* Get to the index root value. */
ir = (INDEX_ROOT*)((u8*)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
@ -324,7 +326,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
rc = ntfs_names_full_collate(uname, uname_len,
(ntfschar*)&ie->key.file_name.file_name,
ie->key.file_name.file_name_length,
CASE_SENSITIVE, vol->upcase, vol->upcase_len);
case_sensitivity, vol->upcase, vol->upcase_len);
/*
* If uname collates before the name of the current entry, there
* is definitely no such name in this index but we might need to
@ -466,7 +468,7 @@ descend_into_child_node:
rc = ntfs_names_full_collate(uname, uname_len,
(ntfschar*)&ie->key.file_name.file_name,
ie->key.file_name.file_name_length,
CASE_SENSITIVE, vol->upcase, vol->upcase_len);
case_sensitivity, vol->upcase, vol->upcase_len);
/*
* If uname collates before the name of the current entry, there
* is definitely no such name in this index but we might need to
@ -545,51 +547,68 @@ u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name)
int uname_len;
ntfschar *uname = (ntfschar*)NULL;
u64 inum;
char *cached_name;
const char *const_name;
if (!NVolCaseSensitive(dir_ni->vol)) {
cached_name = ntfs_uppercase_mbs(name,
dir_ni->vol->upcase, dir_ni->vol->upcase_len);
const_name = cached_name;
} else {
cached_name = (char*)NULL;
const_name = name;
}
if (const_name) {
#if CACHE_LOOKUP_SIZE
struct CACHED_LOOKUP item;
struct CACHED_LOOKUP *cached;
/*
* fetch inode from cache
*/
if (dir_ni->vol->lookup_cache) {
item.name = name;
item.namesize = strlen(name) + 1;
item.parent = dir_ni->mft_no;
cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache(
dir_ni->vol->lookup_cache, GENERIC(&item),
lookup_cache_compare);
if (cached) {
inum = cached->inum;
if (inum == (u64)-1)
errno = ENOENT;
} else {
/* Generate unicode name. */
uname_len = ntfs_mbstoucs(name, &uname);
if (uname_len >= 0) {
inum = ntfs_inode_lookup_by_name(dir_ni,
uname, uname_len);
item.inum = inum;
if (dir_ni->vol->lookup_cache) {
struct CACHED_LOOKUP item;
struct CACHED_LOOKUP *cached;
item.name = const_name;
item.namesize = strlen(const_name) + 1;
item.parent = dir_ni->mft_no;
cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache(
dir_ni->vol->lookup_cache,
GENERIC(&item), lookup_cache_compare);
if (cached) {
inum = cached->inum;
if (inum == (u64)-1)
errno = ENOENT;
} else {
/* Generate unicode name. */
uname_len = ntfs_mbstoucs(name, &uname);
if (uname_len >= 0) {
inum = ntfs_inode_lookup_by_name(dir_ni,
uname, uname_len);
item.inum = inum;
/* enter into cache, even if not found */
ntfs_enter_cache(dir_ni->vol->lookup_cache,
ntfs_enter_cache(dir_ni->vol->lookup_cache,
GENERIC(&item),
lookup_cache_compare);
free(uname);
} else
free(uname);
} else
inum = (s64)-1;
}
} else
#endif
{
/* Generate unicode name. */
uname_len = ntfs_mbstoucs(cached_name, &uname);
if (uname_len >= 0)
inum = ntfs_inode_lookup_by_name(dir_ni,
uname, uname_len);
else
inum = (s64)-1;
}
if (cached_name)
free(cached_name);
} else
#endif
{
/* Generate unicode name. */
uname_len = ntfs_mbstoucs(name, &uname);
if (uname_len >= 0)
inum = ntfs_inode_lookup_by_name(dir_ni,
uname, uname_len);
else
inum = (s64)-1;
}
inum = (s64)-1;
return (inum);
}
@ -604,17 +623,29 @@ void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum)
#if CACHE_LOOKUP_SIZE
struct CACHED_LOOKUP item;
struct CACHED_LOOKUP *cached;
char *cached_name;
if (dir_ni->vol->lookup_cache) {
item.name = name;
item.namesize = strlen(name) + 1;
item.parent = dir_ni->mft_no;
item.inum = inum;
cached = (struct CACHED_LOOKUP*)ntfs_enter_cache(
if (!NVolCaseSensitive(dir_ni->vol)) {
cached_name = ntfs_uppercase_mbs(name,
dir_ni->vol->upcase, dir_ni->vol->upcase_len);
item.name = cached_name;
} else {
cached_name = (char*)NULL;
item.name = name;
}
if (item.name) {
item.namesize = strlen(item.name) + 1;
item.parent = dir_ni->mft_no;
item.inum = inum;
cached = (struct CACHED_LOOKUP*)ntfs_enter_cache(
dir_ni->vol->lookup_cache,
GENERIC(&item), lookup_cache_compare);
if (cached)
cached->inum = inum;
if (cached)
cached->inum = inum;
if (cached_name)
free(cached_name);
}
}
#endif
}
@ -859,6 +890,7 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits,
FILE_NAME_ATTR *fn = &ie->key.file_name;
unsigned dt_type;
BOOL metadata;
ntfschar *loname;
int res;
MFT_REF mref;
@ -888,9 +920,27 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits,
|| !(fn->file_attributes & FILE_ATTR_HIDDEN)))
|| (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol)
|| metadata))) {
res = filldir(dirent, fn->file_name, fn->file_name_length,
fn->file_name_type, *pos,
mref, dt_type);
if (NVolCaseSensitive(dir_ni->vol)) {
res = filldir(dirent, fn->file_name,
fn->file_name_length,
fn->file_name_type, *pos,
mref, dt_type);
} else {
loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length);
if (loname) {
memcpy(loname, fn->file_name,
2*fn->file_name_length);
ntfs_name_locase(loname, fn->file_name_length,
dir_ni->vol->locase,
dir_ni->vol->upcase_len);
res = filldir(dirent, loname,
fn->file_name_length,
fn->file_name_type, *pos,
mref, dt_type);
free(loname);
} else
res = -1;
}
} else
res = 0;
return (res);

View File

@ -389,6 +389,21 @@ void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase,
name[i] = upcase[u];
}
/**
* ntfs_name_locase - Map a Unicode name to its lowercase equivalent
*/
void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase,
const u32 locase_len)
{
u32 i;
u16 u;
if (locase)
for (i = 0; i < name_len; i++)
if ((u = le16_to_cpu(name[i])) < locase_len)
name[i] = locase[u];
}
/**
* ntfs_file_value_upcase - Convert a filename to upper case
* @file_name_attr:
@ -1035,6 +1050,61 @@ err_out:
return -1;
}
/*
* Turn a UTF8 name uppercase
*
* Returns an allocated uppercase name which has to be freed by caller
* or NULL if there is an error (described by errno)
*/
char *ntfs_uppercase_mbs(const char *low,
const ntfschar *upcase, u32 upcase_size)
{
int size;
char *upp;
u32 wc;
int n;
const char *s;
char *t;
size = strlen(low);
upp = (char*)ntfs_malloc(3*size + 1);
if (upp) {
s = low;
t = upp;
do {
n = utf8_to_unicode(&wc, s);
if (n > 0) {
if (wc < upcase_size)
wc = le16_to_cpu(upcase[wc]);
if (wc < 0x80)
*t++ = wc;
else if (wc < 0x800) {
*t++ = (0xc0 | ((wc >> 6) & 0x3f));
*t++ = 0x80 | (wc & 0x3f);
} else if (wc < 0x10000) {
*t++ = 0xe0 | (wc >> 12);
*t++ = 0x80 | ((wc >> 6) & 0x3f);
*t++ = 0x80 | (wc & 0x3f);
} else {
*t++ = 0xf0 | ((wc >> 18) & 7);
*t++ = 0x80 | ((wc >> 12) & 63);
*t++ = 0x80 | ((wc >> 6) & 0x3f);
*t++ = 0x80 | (wc & 0x3f);
}
s += n;
}
} while (n > 0);
if (n < 0) {
free(upp);
upp = (char*)NULL;
errno = EILSEQ;
}
*t = 0;
}
return (upp);
}
/**
* ntfs_upcase_table_build - build the default upcase table for NTFS
* @uc: destination buffer where to store the built table
@ -1106,6 +1176,38 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
}
}
/*
* Build a table for converting to lower case
*
* This is only meaningful when there is a single lower case
* character leading to an upper case one, and currently the
* only exception is the greek letter sigma which has a single
* upper case glyph (code U+03A3), but two lower case glyphs
* (code U+03C3 and U+03C2, the latter to be used at the end
* of a word). In the following implementation the upper case
* sigma will be lowercased as U+03C3.
*/
ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt)
{
ntfschar *lc;
u32 upp;
u32 i;
lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar));
if (lc) {
for (i=0; i<uc_cnt; i++)
lc[i] = cpu_to_le16(i);
for (i=0; i<uc_cnt; i++) {
upp = le16_to_cpu(uc[i]);
if ((upp != i) && (upp < uc_cnt))
lc[upp] = cpu_to_le16(i);
}
} else
ntfs_log_error("Could not build the locase table\n");
return (lc);
}
/**
* ntfs_str2ucs - convert a string to a valid NTFS file name
* @s: input string

View File

@ -202,6 +202,7 @@ static int __ntfs_volume_release(ntfs_volume *v)
ntfs_free_lru_caches(v);
free(v->vol_name);
free(v->upcase);
if (v->locase) free(v->locase);
free(v->attrdef);
free(v);
@ -488,6 +489,9 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags)
ntfs_upcase_table_build(vol->upcase,
vol->upcase_len * sizeof(ntfschar));
/* Default with no locase table and case sensitive file names */
vol->locase = (ntfschar*)NULL;
NVolSetCaseSensitive(vol);
/* by default, all files are shown and not marked hidden */
NVolSetShowSysFiles(vol);
@ -1229,6 +1233,28 @@ int ntfs_set_shown_files(ntfs_volume *vol,
return (res);
}
/*
* Set ignore case mode
*/
int ntfs_set_ignore_case(ntfs_volume *vol)
{
int res;
res = -1;
if (vol && vol->upcase) {
vol->locase = ntfs_locase_table_build(vol->upcase,
vol->upcase_len);
if (vol->locase) {
NVolClearCaseSensitive(vol);
res = 0;
}
}
if (res)
ntfs_log_error("Failed to set ignore_case mode\n");
return (res);
}
/**
* ntfs_mount - open ntfs volume
* @name: name of device/file to open

View File

@ -211,6 +211,7 @@ typedef struct {
BOOL show_sys_files;
BOOL show_hid_files;
BOOL hide_dot_files;
BOOL ignore_case;
BOOL silent;
BOOL recover;
BOOL hiberfile;
@ -3630,6 +3631,7 @@ static int ntfs_fuse_init(void)
static int ntfs_open(const char *device)
{
unsigned long flags = 0;
ntfs_volume *vol;
if (!ctx->blkdev)
flags |= MS_EXCLUSIVE;
@ -3640,28 +3642,31 @@ static int ntfs_open(const char *device)
if (ctx->hiberfile)
flags |= MS_IGNORE_HIBERFILE;
ctx->vol = ntfs_mount(device, flags);
if (!ctx->vol) {
ctx->vol = vol = ntfs_mount(device, flags);
if (!vol) {
ntfs_log_perror("Failed to mount '%s'", device);
goto err_out;
}
if (ntfs_set_shown_files(ctx->vol, ctx->show_sys_files,
if (ntfs_set_shown_files(vol, ctx->show_sys_files,
ctx->show_hid_files, ctx->hide_dot_files))
goto err_out;
if (ctx->ignore_case && ntfs_set_ignore_case(vol))
goto err_out;
ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na);
if (ctx->vol->free_clusters < 0) {
vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na);
if (vol->free_clusters < 0) {
ntfs_log_perror("Failed to read NTFS $Bitmap");
goto err_out;
}
ctx->vol->free_mft_records = ntfs_get_nr_free_mft_records(ctx->vol);
if (ctx->vol->free_mft_records < 0) {
vol->free_mft_records = ntfs_get_nr_free_mft_records(vol);
if (vol->free_mft_records < 0) {
ntfs_log_perror("Failed to calculate free MFT records");
goto err_out;
}
if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) {
if (ctx->hiberfile && ntfs_volume_check_hiberfile(vol, 0)) {
if (errno != EPERM)
goto err_out;
if (ntfs_fuse_rm((fuse_req_t)NULL,FILE_root,"hiberfil.sys"))
@ -3820,6 +3825,10 @@ static char *parse_mount_options(const char *orig_opts)
if (bogus_option_value(val, "hide_dot_files"))
goto err_exit;
ctx->hide_dot_files = TRUE;
} else if (!strcmp(opt, "ignore_case")) {
if (bogus_option_value(val, "ignore_case"))
goto err_exit;
ctx->ignore_case = TRUE;
} else if (!strcmp(opt, "silent")) {
if (bogus_option_value(val, "silent"))
goto err_exit;

View File

@ -150,6 +150,11 @@ Force the mounting even if the NTFS logfile is unclean. The logfile
will be unconditionally cleared. Use this option with caution and for
your own responsibility.
.TP
.B ignore_case
(only with lowntfs-3g) Ignore character case when accessing a file
(\fBFOO\fR, \fBFoo\fR, \fBfoo\fR, etc. designate the same file). All
files are displayed with lower case in directory listings.
.TP
.B remove_hiberfile
Unlike in case of read-only mount, the read-write mount is denied if
the NTFS volume is hibernated. One needs either to resume Windows and