Adapted to ntfs-3g.1.5222-RC

This commit is contained in:
jpandre 2009-01-05 13:28:06 +00:00
parent 4898e594b8
commit d3f3a19866
6 changed files with 487 additions and 341 deletions

View File

@ -23,8 +23,8 @@
# Autoconf
AC_PREREQ(2.59)
AC_INIT([ntfs-3g],[1.2812],[ntfs-3g-devel@lists.sf.net])
LIBNTFS_3G_VERSION="36"
AC_INIT([ntfs-3g],[1.5222-RC],[ntfs-3g-devel@lists.sf.net])
LIBNTFS_3G_VERSION="47"
AC_CONFIG_SRCDIR([src/ntfs-3g.c])
# Environment
@ -107,10 +107,10 @@ AC_ARG_ENABLE(
)
AC_ARG_ENABLE(
[posix-acl],
[AS_HELP_STRING([--disable-posix-acl],[disable POSIX ACL support])],
[posix-acls],
[AS_HELP_STRING([--enable-posix-acls],[enable POSIX ACL support])],
,
[enable_posix_acl="yes"]
[enable_posix_acls="no"]
)
AC_ARG_ENABLE(
@ -316,7 +316,7 @@ test "${enable_device_default_io_ops}" = "no" && AC_DEFINE(
)
test "${enable_mtab}" = "no" && AC_DEFINE([IGNORE_MTAB], [1], [Don't update /etc/mtab])
test "${enable_posix_acl}" = "yes" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support])
test "${enable_posix_acls}" != "no" && AC_DEFINE([POSIXACLS], [1], [POSIX ACL support])
test "${enable_really_static}" = "yes" && enable_library="no"
test "${enable_library}" = "no" && enable_ldconfig="no"

View File

@ -241,6 +241,8 @@ struct _ntfs_volume {
};
extern const char *ntfs_home;
extern ntfs_volume *ntfs_volume_alloc(void);
extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
@ -259,6 +261,9 @@ extern int ntfs_logfile_reset(ntfs_volume *vol);
extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags);
extern int ntfs_volume_error(int err);
extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err);
extern int ntfs_set_locale(void);
#endif /* defined _NTFS_VOLUME_H */

View File

@ -171,7 +171,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL,
0, ctx)) {
ntfs_log_perror("Index root attribute missing in directory inode "
"0x%llx", (unsigned long long)dir_ni->mft_no);
"%lld", (unsigned long long)dir_ni->mft_no);
goto put_err_out;
}
/* Get to the index root value. */
@ -180,7 +180,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
index_block_size = le32_to_cpu(ir->index_block_size);
if (index_block_size < NTFS_BLOCK_SIZE ||
index_block_size & (index_block_size - 1)) {
ntfs_log_debug("Index block size %u is invalid.\n",
ntfs_log_error("Index block size %u is invalid.\n",
(unsigned)index_block_size);
goto put_err_out;
}
@ -197,8 +197,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
index_end)
index_end) {
ntfs_log_error("Index entry out of bounds in inode %lld"
"\n", (unsigned long long)dir_ni->mft_no);
goto put_err_out;
}
/*
* The last entry cannot contain a name. It can however contain
* a pointer to a child node in the B+tree so we just break out.
@ -206,8 +209,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
if (ie->ie_flags & INDEX_ENTRY_END)
break;
if (!le16_to_cpu(ie->length))
if (!le16_to_cpu(ie->length)) {
ntfs_log_error("Zero length index entry in inode %lld"
"\n", (unsigned long long)dir_ni->mft_no);
goto put_err_out;
}
/*
* Not a perfect match, need to do full blown collation so we
* know which way in the B+tree we have to go.
@ -266,8 +272,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
/* Open the index allocation attribute. */
ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
if (!ia_na) {
ntfs_log_perror("Failed to open index allocation attribute. Directory "
"inode 0x%llx is corrupt or driver bug",
ntfs_log_perror("Failed to open index allocation (inode %lld)",
(unsigned long long)dir_ni->mft_no);
goto put_err_out;
}
@ -305,7 +310,7 @@ descend_into_child_node:
}
if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different "
ntfs_log_error("Actual VCN (0x%llx) of index buffer is different "
"from expected VCN (0x%llx).\n",
(long long)sle64_to_cpu(ia->index_block_vcn),
(long long)vcn);
@ -313,7 +318,7 @@ descend_into_child_node:
goto close_err_out;
}
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx "
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx "
"has a size (%u) differing from the directory "
"specified size (%u).\n", (long long)vcn,
(unsigned long long)dir_ni->mft_no,
@ -324,7 +329,7 @@ descend_into_child_node:
}
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
if (index_end > (u8*)ia + index_block_size) {
ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode "
ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode "
"0x%llx exceeds maximum size.\n",
(long long)vcn, (unsigned long long)dir_ni->mft_no);
errno = EIO;
@ -345,9 +350,9 @@ descend_into_child_node:
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
index_end) {
ntfs_log_debug("Index entry out of bounds in directory "
"inode 0x%llx.\n",
(unsigned long long)dir_ni->mft_no);
ntfs_log_error("Index entry out of bounds in directory "
"inode %lld.\n",
(unsigned long long)dir_ni->mft_no);
errno = EIO;
goto close_err_out;
}
@ -360,6 +365,8 @@ descend_into_child_node:
if (!le16_to_cpu(ie->length)) {
errno = EIO;
ntfs_log_error("Zero length index entry in inode %lld"
"\n", (unsigned long long)dir_ni->mft_no);
goto close_err_out;
}
/*
@ -406,8 +413,8 @@ descend_into_child_node:
*/
if (ie->ie_flags & INDEX_ENTRY_NODE) {
if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) {
ntfs_log_debug("Index entry with child node found in a leaf "
"node in directory inode 0x%llx.\n",
ntfs_log_error("Index entry with child node found in a leaf "
"node in directory inode %lld.\n",
(unsigned long long)dir_ni->mft_no);
errno = EIO;
goto close_err_out;
@ -416,8 +423,8 @@ descend_into_child_node:
vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
if (vcn >= 0)
goto descend_into_child_node;
ntfs_log_debug("Negative child node vcn in directory inode "
"0x%llx.\n", (unsigned long long)dir_ni->mft_no);
ntfs_log_error("Negative child node vcn in directory inode "
"0x%llx.\n", (unsigned long long)dir_ni->mft_no);
errno = EIO;
goto close_err_out;
}
@ -486,7 +493,7 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
ascii = strdup(pathname);
if (!ascii) {
ntfs_log_debug("Out of memory.\n");
ntfs_log_error("Out of memory.\n");
err = ENOMEM;
goto out;
}
@ -550,7 +557,8 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
len = ntfs_mbstoucs(p, &unicode);
if (len < 0) {
ntfs_log_debug("Couldn't convert name to Unicode: %s.\n", p);
ntfs_log_perror("Couldn't convert filename to Unicode: "
"'%s'.\n", p);
err = errno;
goto close;
} else if (len > NTFS_MAX_NAME_LEN) {
@ -735,21 +743,21 @@ static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
if (!ctx)
return ERR_MREF(-1);
if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
ntfs_log_debug("No file name found in inode 0x%llx. Corrupt "
"inode.\n", (unsigned long long)ni->mft_no);
ntfs_log_error("No file name found in inode %lld\n",
(unsigned long long)ni->mft_no);
goto err_out;
}
if (ctx->attr->non_resident) {
ntfs_log_debug("File name attribute must be resident. Corrupt inode "
"0x%llx.\n", (unsigned long long)ni->mft_no);
ntfs_log_error("File name attribute must be resident (inode "
"%lld)\n", (unsigned long long)ni->mft_no);
goto io_err_out;
}
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) >
(u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n",
(unsigned long long)ni->mft_no);
ntfs_log_error("Corrupt file name attribute in inode %lld.\n",
(unsigned long long)ni->mft_no);
goto io_err_out;
}
mref = le64_to_cpu(fn->parent_directory);
@ -809,7 +817,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
vol = dir_ni->vol;
ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n",
ntfs_log_trace("Entering for inode %lld, *pos 0x%llx.\n",
(unsigned long long)dir_ni->mft_no, (long long)*pos);
/* Open the index allocation attribute. */
@ -817,7 +825,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
if (!ia_na) {
if (errno != ENOENT) {
ntfs_log_perror("Failed to open index allocation attribute. "
"Directory inode 0x%llx is corrupt or bug",
"Directory inode %lld is corrupt or bug",
(unsigned long long)dir_ni->mft_no);
return -1;
}
@ -867,7 +875,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL,
0, ctx)) {
ntfs_log_perror("Index root attribute missing in directory inode "
"0x%llx", (unsigned long long)dir_ni->mft_no);
"%lld", (unsigned long long)dir_ni->mft_no);
goto dir_err_out;
}
/* Get to the index root value. */
@ -1034,7 +1042,7 @@ find_next_index_buffer:
goto dir_err_out;
}
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx "
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld "
"has a size (%u) differing from the directory "
"specified size (%u).\n", (long long)ia_start >>
index_vcn_size_bits,
@ -1046,7 +1054,7 @@ find_next_index_buffer:
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
if (index_end > (u8*)ia + index_block_size) {
ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode "
"0x%llx exceeds maximum size.\n",
"%lld exceeds maximum size.\n",
(long long)ia_start >> index_vcn_size_bits,
(unsigned long long)dir_ni->mft_no);
goto dir_err_out;
@ -1068,7 +1076,7 @@ find_next_index_buffer:
(u8*)ie + le16_to_cpu(ie->key_length) >
index_end) {
ntfs_log_error("Index entry out of bounds in directory inode "
"0x%llx.\n", (unsigned long long)dir_ni->mft_no);
"%lld.\n", (unsigned long long)dir_ni->mft_no);
goto dir_err_out;
}
/* The last entry cannot contain a name. */

View File

@ -387,293 +387,323 @@ int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
err_val, ic, upcase, upcase_len);
}
/*
#
# NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough
# for now]) for path names, but the Unicode code points need to be
# converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI,
# glibc does this even without a locale in a hard-coded fashion as that
# appears to be is easy because the low 7-bit ASCII range appears to be
# available # in all charsets but it does not convert anything if
# there was some error with the locale setup or none set up like
# when mount is called during early boot where he (by policy) do
# not use locales (and may be not available if /usr is not yet mounted),
# so this patch fixes the resulting issues for systems which use
# UTF-8 and for others, specifying the locale in fstab brings them
# the encoding which they want.
#
# If no locale is defined or there was a problem with setting one
# up and whenever nl_langinfo(CODESET) returns a sting starting with
# "ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix
# the bug where NTFS-3G does not show any path names which include
# international characters!!! (and also fails on creating them) as result.
#
# Author: Bernhard Kaindl <bk@suse.de>
#
*/
/* Return the amount of 16-bit elements in UTF-16LE needed (without
* the terminating null to store given UTF-8 string and -1 if it does
* not fit into PATH_MAX
*
* JPA : made compliant with RFC3629 / RFC2781
*/
static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len)
{
int i;
int count = 0;
BOOL surrog;
surrog = FALSE;
for (i = 0; i < ins_len && ins[i]; i++) {
unsigned short c = le16_to_cpu(ins[i]);
if (surrog) {
if ((c >= 0xdc00) && (c < 0xe000)) {
surrog = FALSE;
count += 4;
} else goto fail;
} else
if (c < 0x80)
count++;
else if (c < 0x800)
count += 2;
else if (c < 0xd800)
count += 3;
else if (c < 0xdc00)
surrog = TRUE;
#if NOREVBOM
else if ((c >= 0xe000) && (c < 0xfffe))
#else
else if (c >= 0xe000)
#endif
count += 3;
else goto fail;
if (count > outs_len)
goto fail;
}
if (surrog) goto fail;
return count;
fail:
return -1;
}
/*
* ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string
* @ins: input utf16 string buffer
* @ins_len: length of input string in utf16 characters
* @outs: on return contains the (allocated) output multibyte string
* @outs_len: length of output buffer in bytes
*
* JPA : made compliant with RFC3629 / RFC2781
*/
static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len,
char **outs, int outs_len)
{
char *t;
int i, size;
ntfschar halfpair;
halfpair = 0;
if (!*outs)
outs_len = PATH_MAX;
size = utf16_to_utf8_size(ins, ins_len, outs_len);
if (size < 0) {
errno = ENAMETOOLONG;
goto fail;
}
if (!*outs)
*outs = ntfs_malloc((outs_len = size + 1));
t = *outs;
for (i = 0; i < ins_len && ins[i]; i++) {
unsigned short c = le16_to_cpu(ins[i]);
/* size not double-checked */
if (halfpair) {
if ((c >= 0xdc00) && (c < 0xe000)) {
*t++ = 0xf0 + (((halfpair + 64) >> 8) & 7);
*t++ = 0x80 + (((halfpair + 64) >> 2) & 63);
*t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4);
*t++ = 0x80 + (c & 63);
halfpair = 0;
} else goto fail;
} else if (c < 0x80) {
*t++ = c;
} else {
if (c < 0x800) {
*t++ = (0xc0 | ((c >> 6) & 0x3f));
*t++ = 0x80 | (c & 0x3f);
} else if (c < 0xd800) {
*t++ = 0xe0 | (c >> 12);
*t++ = 0x80 | ((c >> 6) & 0x3f);
*t++ = 0x80 | (c & 0x3f);
} else if (c < 0xdc00)
halfpair = c;
else if (c >= 0xe000) {
*t++ = 0xe0 | (c >> 12);
*t++ = 0x80 | ((c >> 6) & 0x3f);
*t++ = 0x80 | (c & 0x3f);
} else goto fail;
}
}
*t = '\0';
return t - *outs;
fail:
return -1;
}
/* Return the amount of 16-bit elements in UTF-16LE needed (without
* the terminating null to store given UTF-8 string and -1 if it does
* not fit into PATH_MAX
*
* Note : this does not check whether the input sequence is a valid utf8
* string, and should be used only in context where such check is made
*
* JPA : made compliant with RFC3629 / RFC2781
*
*/
static int utf8_to_utf16_size(const char *s)
{
unsigned int byte;
size_t count = 0;
while ((byte = *((const unsigned char *)s++))) {
if (++count >= PATH_MAX || byte >= 0xF5)
goto fail;
if (!*s) break;
if (byte >= 0xC0) s++;
if (!*s) break;
if (byte >= 0xE0) s++;
if (!*s) break;
if (byte >= 0xF0) {
s++;
if (++count >= PATH_MAX)
goto fail;
}
}
return count;
fail:
return -1;
}
/* This converts one UTF-8 sequence to cpu-endian Unicode value
* within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF
* Returns the number of used utf8 bytes or -1 if sequence is invalid
*
* JPA : made compliant with RFC3629 / RFC2781
*/
static int utf8_to_unicode(u32 *wc, const char *s)
{
unsigned int byte = *((const unsigned char *)s);
/* single byte */
if (byte == 0) {
*wc = (u32) 0;
return 0;
} else if (byte < 0x80) {
*wc = (u32) byte;
return 1;
/* double byte */
} else if (byte < 0xc2) {
goto fail;
} else if (byte < 0xE0) {
if (strlen(s) < 2)
goto fail;
if ((s[1] & 0xC0) == 0x80) {
*wc = ((u32)(byte & 0x1F) << 6)
| ((u32)(s[1] & 0x3F));
return 2;
} else
goto fail;
/* three-byte */
} else if (byte < 0xF0) {
if (strlen(s) < 3)
goto fail;
if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) {
*wc = ((u32)(byte & 0x0F) << 12)
| ((u32)(s[1] & 0x3F) << 6)
| ((u32)(s[2] & 0x3F));
/* Check valid ranges */
#if NOREVBOM
if (((*wc >= 0x800) && (*wc <= 0xD7FF))
|| ((*wc >= 0xe000) && (*wc <= 0xFFFD)))
return 3;
#else
if (((*wc >= 0x800) && (*wc <= 0xD7FF))
|| ((*wc >= 0xe000) && (*wc <= 0xFFFF)))
return 3;
#endif
}
goto fail;
/* four-byte */
} else if (byte < 0xF5) {
if (strlen(s) < 4)
goto fail;
if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)
&& ((s[3] & 0xC0) == 0x80)) {
*wc = ((u32)(byte & 0x07) << 18)
| ((u32)(s[1] & 0x3F) << 12)
| ((u32)(s[2] & 0x3F) << 6)
| ((u32)(s[3] & 0x3F));
/* Check valid ranges */
if ((*wc <= 0x10ffff) && (*wc >= 0x10000))
return 4;
}
goto fail;
}
fail:
return -1;
}
/**
* ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string
* @ins: input multibyte string buffer
* @outs: on return contains the (allocated) output utf16 string
* @outs_len: length of output buffer in utf16 characters
*
* JPA : made compliant with RFC3629 / RFC2781
*/
static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs)
{
const char *t = ins;
u32 wc;
ntfschar *outpos;
int shorts = utf8_to_utf16_size(ins);
if (shorts < 0) {
errno = EILSEQ;
goto fail;
}
if (!*outs)
*outs = ntfs_malloc((shorts+1) * sizeof(ntfschar));
outpos = *outs;
while(1) {
int m = utf8_to_unicode(&wc, t);
if (m < 0) {
errno = EILSEQ;
goto fail;
}
if (wc < 0x10000)
*outpos++ = cpu_to_le16(wc);
else {
wc -= 0x10000;
*outpos++ = cpu_to_le16((wc >> 10) + 0xd800);
*outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00);
}
if (m == 0)
break;
t += m;
}
return --outpos - *outs;
fail:
return -1;
}
/*
NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough
for now]) for path names, but the Unicode code points need to be
converted before a path can be accessed under NTFS. For 7 bit ASCII/ANSI,
glibc does this even without a locale in a hard-coded fashion as that
appears to be is easy because the low 7-bit ASCII range appears to be
available in all charsets but it does not convert anything if
there was some error with the locale setup or none set up like
when mount is called during early boot where he (by policy) do
not use locales (and may be not available if /usr is not yet mounted),
so this patch fixes the resulting issues for systems which use
UTF-8 and for others, specifying the locale in fstab brings them
the encoding which they want.
If no locale is defined or there was a problem with setting one
up and whenever nl_langinfo(CODESET) returns a sting starting with
"ANSI", use an internal UCS-2LE <-> UTF-8 codeset converter to fix
the bug where NTFS-3G does not show any path names which include
international characters!!! (and also fails on creating them) as result.
Author: Bernhard Kaindl <bk@suse.de>
Jean-Pierre Andre made it compliant with RFC3629/RFC2781.
*/
/*
* Return the amount of 8-bit elements in UTF-8 needed (without the terminating
* null) to store a given UTF-16LE string.
*
* Return -1 with errno set if string has invalid byte sequence or too long.
*/
static int utf16_to_utf8_size(const ntfschar *ins, const int ins_len, int outs_len)
{
int i, ret = -1;
int count = 0;
BOOL surrog;
surrog = FALSE;
for (i = 0; i < ins_len && ins[i]; i++) {
unsigned short c = le16_to_cpu(ins[i]);
if (surrog) {
if ((c >= 0xdc00) && (c < 0xe000)) {
surrog = FALSE;
count += 4;
} else
goto fail;
} else
if (c < 0x80)
count++;
else if (c < 0x800)
count += 2;
else if (c < 0xd800)
count += 3;
else if (c < 0xdc00)
surrog = TRUE;
#if NOREVBOM
else if ((c >= 0xe000) && (c < 0xfffe))
#else
else if (c >= 0xe000)
#endif
count += 3;
else
goto fail;
if (count > outs_len) {
errno = ENAMETOOLONG;
goto out;
}
}
if (surrog)
goto fail;
ret = count;
out:
return ret;
fail:
errno = EILSEQ;
goto out;
}
/*
* ntfs_utf16_to_utf8 - convert a little endian UTF16LE string to an UTF-8 string
* @ins: input utf16 string buffer
* @ins_len: length of input string in utf16 characters
* @outs: on return contains the (allocated) output multibyte string
* @outs_len: length of output buffer in bytes
*
* Return -1 with errno set if string has invalid byte sequence or too long.
*/
static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len,
char **outs, int outs_len)
{
char *t;
int i, size, ret = -1;
ntfschar halfpair;
halfpair = 0;
if (!*outs)
outs_len = PATH_MAX;
size = utf16_to_utf8_size(ins, ins_len, outs_len);
if (size < 0)
goto out;
if (!*outs) {
outs_len = size + 1;
*outs = ntfs_malloc(outs_len);
if (!*outs)
goto out;
}
t = *outs;
for (i = 0; i < ins_len && ins[i]; i++) {
unsigned short c = le16_to_cpu(ins[i]);
/* size not double-checked */
if (halfpair) {
if ((c >= 0xdc00) && (c < 0xe000)) {
*t++ = 0xf0 + (((halfpair + 64) >> 8) & 7);
*t++ = 0x80 + (((halfpair + 64) >> 2) & 63);
*t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4);
*t++ = 0x80 + (c & 63);
halfpair = 0;
} else
goto fail;
} else if (c < 0x80) {
*t++ = c;
} else {
if (c < 0x800) {
*t++ = (0xc0 | ((c >> 6) & 0x3f));
*t++ = 0x80 | (c & 0x3f);
} else if (c < 0xd800) {
*t++ = 0xe0 | (c >> 12);
*t++ = 0x80 | ((c >> 6) & 0x3f);
*t++ = 0x80 | (c & 0x3f);
} else if (c < 0xdc00)
halfpair = c;
else if (c >= 0xe000) {
*t++ = 0xe0 | (c >> 12);
*t++ = 0x80 | ((c >> 6) & 0x3f);
*t++ = 0x80 | (c & 0x3f);
} else
goto fail;
}
}
*t = '\0';
ret = t - *outs;
out:
return ret;
fail:
errno = EILSEQ;
goto out;
}
/*
* Return the amount of 16-bit elements in UTF-16LE needed
* (without the terminating null) to store given UTF-8 string.
*
* Return -1 with errno set if it's longer than PATH_MAX or string is invalid.
*
* Note: This does not check whether the input sequence is a valid utf8 string,
* and should be used only in context where such check is made!
*/
static int utf8_to_utf16_size(const char *s)
{
int ret = -1;
unsigned int byte;
size_t count = 0;
while ((byte = *((const unsigned char *)s++))) {
if (++count >= PATH_MAX)
goto fail;
if (byte >= 0xF5) {
errno = EILSEQ;
goto out;
}
if (!*s)
break;
if (byte >= 0xC0)
s++;
if (!*s)
break;
if (byte >= 0xE0)
s++;
if (!*s)
break;
if (byte >= 0xF0) {
s++;
if (++count >= PATH_MAX)
goto fail;
}
}
ret = count;
out:
return ret;
fail:
errno = ENAMETOOLONG;
goto out;
}
/*
* This converts one UTF-8 sequence to cpu-endian Unicode value
* within range U+0 .. U+10ffff and excluding U+D800 .. U+DFFF
*
* Return the number of used utf8 bytes or -1 with errno set
* if sequence is invalid.
*/
static int utf8_to_unicode(u32 *wc, const char *s)
{
unsigned int byte = *((const unsigned char *)s);
/* single byte */
if (byte == 0) {
*wc = (u32) 0;
return 0;
} else if (byte < 0x80) {
*wc = (u32) byte;
return 1;
/* double byte */
} else if (byte < 0xc2) {
goto fail;
} else if (byte < 0xE0) {
if (strlen(s) < 2)
goto fail;
if ((s[1] & 0xC0) == 0x80) {
*wc = ((u32)(byte & 0x1F) << 6)
| ((u32)(s[1] & 0x3F));
return 2;
} else
goto fail;
/* three-byte */
} else if (byte < 0xF0) {
if (strlen(s) < 3)
goto fail;
if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) {
*wc = ((u32)(byte & 0x0F) << 12)
| ((u32)(s[1] & 0x3F) << 6)
| ((u32)(s[2] & 0x3F));
/* Check valid ranges */
#if NOREVBOM
if (((*wc >= 0x800) && (*wc <= 0xD7FF))
|| ((*wc >= 0xe000) && (*wc <= 0xFFFD)))
return 3;
#else
if (((*wc >= 0x800) && (*wc <= 0xD7FF))
|| ((*wc >= 0xe000) && (*wc <= 0xFFFF)))
return 3;
#endif
}
goto fail;
/* four-byte */
} else if (byte < 0xF5) {
if (strlen(s) < 4)
goto fail;
if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)
&& ((s[3] & 0xC0) == 0x80)) {
*wc = ((u32)(byte & 0x07) << 18)
| ((u32)(s[1] & 0x3F) << 12)
| ((u32)(s[2] & 0x3F) << 6)
| ((u32)(s[3] & 0x3F));
/* Check valid ranges */
if ((*wc <= 0x10ffff) && (*wc >= 0x10000))
return 4;
}
goto fail;
}
fail:
errno = EILSEQ;
return -1;
}
/**
* ntfs_utf8_to_utf16 - convert a UTF-8 string to a UTF-16LE string
* @ins: input multibyte string buffer
* @outs: on return contains the (allocated) output utf16 string
* @outs_len: length of output buffer in utf16 characters
*
* Return -1 with errno set.
*/
static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs)
{
const char *t = ins;
u32 wc;
ntfschar *outpos;
int shorts, ret = -1;
shorts = utf8_to_utf16_size(ins);
if (shorts < 0)
goto fail;
if (!*outs) {
*outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar));
if (!*outs)
goto fail;
}
outpos = *outs;
while(1) {
int m = utf8_to_unicode(&wc, t);
if (m < 0)
goto fail;
if (wc < 0x10000)
*outpos++ = cpu_to_le16(wc);
else {
wc -= 0x10000;
*outpos++ = cpu_to_le16((wc >> 10) + 0xd800);
*outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00);
}
if (m == 0)
break;
t += m;
}
ret = --outpos - *outs;
fail:
return ret;
}
/**
* ntfs_ucstombs - convert a little endian Unicode string to a multibyte string
* @ins: input Unicode string buffer
@ -1041,9 +1071,8 @@ void ntfs_ucsfree(ntfschar *ucs)
}
/*
* Define the character encoding to be used
*
* Using UTF-8 unless specified otherwise
* Define the character encoding to be used.
* Use UTF-8 unless specified otherwise.
*/
int ntfs_set_char_encoding(const char *locale)
@ -1059,6 +1088,6 @@ int ntfs_set_char_encoding(const char *locale)
ntfs_log_error("Invalid locale, encoding to UTF-8\n");
use_utf8 = 1;
}
return (0); /* always successful */
return 0; /* always successful */
}

View File

@ -49,6 +49,9 @@
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include "volume.h"
#include "attrib.h"
@ -67,6 +70,66 @@
#define PATH_MAX 4096
#endif
const char *ntfs_home =
"Ntfs-3g news, support and information: http://ntfs-3g.org\n";
static const char *invalid_ntfs_msg =
"The device '%s' doesn't seem to have a valid NTFS.\n"
"Maybe the wrong device is used? Or the whole disk instead of a\n"
"partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?\n";
static const char *corrupt_volume_msg =
"NTFS is either inconsistent, or there is a hardware fault, or it's a\n"
"SoftRAID/FakeRAID hardware. In the first case run chkdsk /f on Windows\n"
"then reboot into Windows twice. The usage of the /f parameter is very\n"
"important! If the device is a SoftRAID/FakeRAID then first activate\n"
"it and mount a different device under the /dev/mapper/ directory, (e.g.\n"
"/dev/mapper/nvidia_eahaabcc1). Please see the 'dmraid' documentation\n"
"for more details.\n";
static const char *hibernated_volume_msg =
"The NTFS partition is hibernated. Please resume and shutdown Windows\n"
"properly, or mount the volume read-only with the 'ro' mount option, or\n"
"mount the volume read-write with the 'remove_hiberfile' mount option.\n"
"For example type on the command line:\n"
"\n"
" mount -t ntfs-3g -o remove_hiberfile %s %s\n"
"\n";
static const char *unclean_journal_msg =
"Mount is denied because NTFS is marked to be in use. Choose one action:\n"
"\n"
"Choice 1: If you have Windows then disconnect the external devices by\n"
" clicking on the 'Safely Remove Hardware' icon in the Windows\n"
" taskbar then shutdown Windows cleanly.\n"
"\n"
"Choice 2: If you don't have Windows then you can use the 'force' option for\n"
" your own responsibility. For example type on the command line:\n";
static const char *opened_volume_msg =
"Mount is denied because the NTFS volume is already exclusively opened.\n"
"The volume may be already mounted, or another software may use it which\n"
"could be identified for example by the help of the 'fuser' command.\n";
static const char *fakeraid_msg =
"Either the device is missing or it's powered down, or you have\n"
"SoftRAID hardware and must use an activated, different device under\n"
"/dev/mapper/, (e.g. /dev/mapper/nvidia_eahaabcc1) to mount NTFS.\n"
"Please see the 'dmraid' documentation for help.\n";
static const char *access_denied_msg =
"Please check '%s' and the ntfs-3g binary permissions,\n"
"and the mounting user ID. More explanation is provided at\n"
"http://ntfs-3g.org/support.html#unprivileged\n";
static const char *forced_mount_msg =
"\n"
" mount -t ntfs-3g -o force %s %s\n"
"\n"
" Or add the option to the relevant row in the /etc/fstab file:\n"
"\n"
" %s %s ntfs-3g force 0 0\n";
/**
* ntfs_volume_alloc - Create an NTFS volume object and initialise it
*
@ -1475,3 +1538,47 @@ int ntfs_volume_error(int err)
return ret;
}
void ntfs_mount_error(const char *volume, const char *mntpoint, int err)
{
switch (err) {
case NTFS_VOLUME_NOT_NTFS:
ntfs_log_error(invalid_ntfs_msg, volume);
break;
case NTFS_VOLUME_CORRUPT:
ntfs_log_error("%s", corrupt_volume_msg);
break;
case NTFS_VOLUME_HIBERNATED:
ntfs_log_error(hibernated_volume_msg, volume, mntpoint);
break;
case NTFS_VOLUME_UNCLEAN_UNMOUNT:
ntfs_log_error("%s", unclean_journal_msg);
ntfs_log_error(forced_mount_msg, volume, mntpoint,
volume, mntpoint);
break;
case NTFS_VOLUME_LOCKED:
ntfs_log_error("%s", opened_volume_msg);
break;
case NTFS_VOLUME_RAID:
ntfs_log_error("%s", fakeraid_msg);
break;
case NTFS_VOLUME_NO_PRIVILEGE:
ntfs_log_error(access_denied_msg, volume);
break;
}
}
int ntfs_set_locale(void)
{
const char *locale;
locale = setlocale(LC_ALL, "");
if (!locale) {
locale = setlocale(LC_ALL, NULL);
ntfs_log_error("Couldn't set local environment, using default "
"'%s'.\n", locale);
return 1;
}
return 0;
}

View File

@ -90,7 +90,6 @@
#include "unistr.h"
#include "layout.h"
#include "index.h"
#include "utils.h"
#include "ntfstime.h"
#include "security.h"
#include "reparse.h"
@ -161,11 +160,6 @@ static char def_opts[] = "silent,allow_other,nonempty,";
static ntfs_fuse_context_t *ctx;
static u32 ntfs_sequence;
static const char *locale_msg =
"WARNING: Couldn't set locale to '%s' thus some file names may not\n"
" be correct or visible. Please see the potential solution at\n"
" http://ntfs-3g.org/support.html#locale\n";
static const char *usage_msg =
"\n"
"%s %s %s %d - Third Generation NTFS Driver\n"
@ -176,8 +170,8 @@ static const char *usage_msg =
"\n"
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
"\n"
"Options: ro (read-only mount), force, remove_hiberfile, locale=,\n"
" uid=, gid=, umask=, fmask=, dmask=, streams_interface=.\n"
"Options: ro (read-only mount), force, remove_hiberfile, uid=,\n"
" gid=, umask=, fmask=, dmask=, streams_interface=.\n"
" Please see the details in the manual.\n"
"\n"
"Examples: ntfs-3g -o force /dev/sda1 /mnt/windows\n"
@ -297,7 +291,10 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)),
if (!vol)
return -ENODEV;
/* File system block size, used for optimal transfer block size. */
/*
* File system block size. Used to calculate used/free space by df.
* Incorrectly documented as "optimal transfer block size".
*/
sfs->f_bsize = vol->cluster_size;
/* Fundamental file system block size, used as the unit. */
@ -667,7 +664,7 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx,
if (ntfs_ucstombs(name, name_len, &filename, 0) < 0) {
ntfs_log_perror("Skipping unrepresentable filename (inode %llu)",
(unsigned long long)MREF(mref));
return 0;
return -1;
}
if (ntfs_fuse_is_named_data_stream(filename)) {
@ -2541,10 +2538,10 @@ static char *parse_mount_options(const char *orig_opts)
goto err_exit;
ctx->hiberfile = TRUE;
} else if (!strcmp(opt, "locale")) {
/* option "locale" kept undocumented */
if (missing_option_value(val, "locale"))
goto err_exit;
if (ntfs_set_char_encoding(val))
ntfs_log_error(locale_msg, val);
ntfs_set_char_encoding(val);
} else if (!strcmp(opt, "streams_interface")) {
if (missing_option_value(val, "streams_interface"))
goto err_exit;
@ -2985,7 +2982,7 @@ int main(int argc, char *argv[])
if (drop_privs())
return NTFS_VOLUME_NO_PRIVILEGE;
utils_set_locale();
ntfs_set_locale();
ntfs_log_set_handler(ntfs_log_handler_stderr);
if (parse_options(argc, argv)) {
@ -3123,7 +3120,7 @@ int main(int argc, char *argv[])
fuse_unmount(opts.mnt_point, ctx->fc);
fuse_destroy(fh);
err_out:
utils_mount_error(opts.device, opts.mnt_point, err);
ntfs_mount_error(opts.device, opts.mnt_point, err);
err2:
ntfs_close();
free(ctx);