From d3f3a19866f5d670c65064ffdf066d8a859510d5 Mon Sep 17 00:00:00 2001 From: jpandre Date: Mon, 5 Jan 2009 13:28:06 +0000 Subject: [PATCH] Adapted to ntfs-3g.1.5222-RC --- configure.ac | 12 +- include/ntfs-3g/volume.h | 5 + libntfs-3g/dir.c | 68 +++-- libntfs-3g/unistr.c | 611 ++++++++++++++++++++------------------- libntfs-3g/volume.c | 107 +++++++ src/ntfs-3g.c | 25 +- 6 files changed, 487 insertions(+), 341 deletions(-) diff --git a/configure.ac b/configure.ac index 58b96ffa..89558a8e 100644 --- a/configure.ac +++ b/configure.ac @@ -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" diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 30d062c0..845457d7 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -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 */ diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index e5fbb85f..d12c5cc8 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -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. */ diff --git a/libntfs-3g/unistr.c b/libntfs-3g/unistr.c index fe7d9e17..ea0d4033 100644 --- a/libntfs-3g/unistr.c +++ b/libntfs-3g/unistr.c @@ -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 -# -*/ - - -/* 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 + 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 */ } diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 49cac4de..e0b7a4ad 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -49,6 +49,9 @@ #ifdef HAVE_LIMITS_H #include #endif +#ifdef HAVE_LOCALE_H +#include +#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; +} + diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3db0937c..35738158 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -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[,...]] \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);