enabled case insensitive file names in lowntfs-3g
parent
81f1de0559
commit
693aa8780d
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
140
libntfs-3g/dir.c
140
libntfs-3g/dir.c
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue