summaryrefslogtreecommitdiff
Diffstat
-rwxr-xr-xAUTHORS23
-rwxr-xr-xAndroid.mk89
-rwxr-xr-xAndroid.mk.bak89
-rwxr-xr-xCOPYING340
-rwxr-xr-xCOPYING.LIB482
-rwxr-xr-xCREDITS41
-rwxr-xr-xChangeLog7
-rwxr-xr-xINSTALL237
-rwxr-xr-xMakefile715
-rwxr-xr-xMakefile.am66
-rwxr-xr-xMakefile.in722
-rwxr-xr-xNEWS5
-rwxr-xr-xREADME68
-rwxr-xr-xaclocal.m47723
-rwxr-xr-xautogen.sh22
-rwxr-xr-xcompile142
-rwxr-xr-xconfig.guess1536
-rwxr-xr-xconfig.h361
-rwxr-xr-xconfig.h.in350
-rwxr-xr-xconfig.log3290
-rwxr-xr-xconfig.status1220
-rwxr-xr-xconfig.sub1658
-rwxr-xr-xconfigure25302
-rwxr-xr-xconfigure.ac441
-rwxr-xr-xdepcomp589
-rwxr-xr-xinclude/Makefile.am4
-rwxr-xr-xinclude/Makefile.in503
-rwxr-xr-xinclude/fuse-lite/Makefile.am10
-rwxr-xr-xinclude/fuse-lite/Makefile.in400
-rwxr-xr-xinclude/fuse-lite/fuse.h654
-rwxr-xr-xinclude/fuse-lite/fuse_common.h193
-rwxr-xr-xinclude/fuse-lite/fuse_kernel.h422
-rwxr-xr-xinclude/fuse-lite/fuse_lowlevel.h1375
-rwxr-xr-xinclude/fuse-lite/fuse_lowlevel_compat.h16
-rwxr-xr-xinclude/fuse-lite/fuse_opt.h261
-rwxr-xr-xinclude/ntfs-3g/Makefile.am45
-rwxr-xr-xinclude/ntfs-3g/Makefile.in471
-rwxr-xr-xinclude/ntfs-3g/acls.h199
-rwxr-xr-xinclude/ntfs-3g/attrib.h358
-rwxr-xr-xinclude/ntfs-3g/attrlist.h51
-rwxr-xr-xinclude/ntfs-3g/bitmap.h96
-rwxr-xr-xinclude/ntfs-3g/bootsect.h42
-rwxr-xr-xinclude/ntfs-3g/cache.h115
-rwxr-xr-xinclude/ntfs-3g/collate.h34
-rwxr-xr-xinclude/ntfs-3g/compat.h69
-rwxr-xr-xinclude/ntfs-3g/compress.h39
-rwxr-xr-xinclude/ntfs-3g/debug.h47
-rwxr-xr-xinclude/ntfs-3g/device.h128
-rwxr-xr-xinclude/ntfs-3g/device_io.h77
-rwxr-xr-xinclude/ntfs-3g/dir.h128
-rwxr-xr-xinclude/ntfs-3g/efs.h30
-rwxr-xr-xinclude/ntfs-3g/endians.h203
-rwxr-xr-xinclude/ntfs-3g/index.h167
-rwxr-xr-xinclude/ntfs-3g/inode.h225
-rwxr-xr-xinclude/ntfs-3g/layout.h2661
-rwxr-xr-xinclude/ntfs-3g/lcnalloc.h50
-rwxr-xr-xinclude/ntfs-3g/logfile.h394
-rwxr-xr-xinclude/ntfs-3g/logging.h118
-rwxr-xr-xinclude/ntfs-3g/mft.h132
-rwxr-xr-xinclude/ntfs-3g/misc.h30
-rwxr-xr-xinclude/ntfs-3g/mst.h34
-rwxr-xr-xinclude/ntfs-3g/ntfstime.h131
-rwxr-xr-xinclude/ntfs-3g/object_id.h35
-rwxr-xr-xinclude/ntfs-3g/param.h63
-rwxr-xr-xinclude/ntfs-3g/reparse.h39
-rwxr-xr-xinclude/ntfs-3g/runlist.h89
-rwxr-xr-xinclude/ntfs-3g/security.h353
-rwxr-xr-xinclude/ntfs-3g/support.h85
-rwxr-xr-xinclude/ntfs-3g/types.h124
-rwxr-xr-xinclude/ntfs-3g/unistr.h116
-rwxr-xr-xinclude/ntfs-3g/volume.h277
-rwxr-xr-xinstall-sh519
-rwxr-xr-xlibfuse-lite/Makefile.am28
-rwxr-xr-xlibfuse-lite/Makefile.in572
-rwxr-xr-xlibfuse-lite/fuse.c2789
-rwxr-xr-xlibfuse-lite/fuse_i.h25
-rwxr-xr-xlibfuse-lite/fuse_kern_chan.c96
-rwxr-xr-xlibfuse-lite/fuse_loop.c40
-rwxr-xr-xlibfuse-lite/fuse_lowlevel.c1395
-rwxr-xr-xlibfuse-lite/fuse_misc.h106
-rwxr-xr-xlibfuse-lite/fuse_opt.c368
-rwxr-xr-xlibfuse-lite/fuse_session.c183
-rwxr-xr-xlibfuse-lite/fuse_signals.c73
-rwxr-xr-xlibfuse-lite/fusermount.c772
-rwxr-xr-xlibfuse-lite/helper.c40
-rwxr-xr-xlibfuse-lite/mount.c256
-rwxr-xr-xlibfuse-lite/mount_util.c219
-rwxr-xr-xlibfuse-lite/mount_util.h22
-rwxr-xr-xlibntfs-3g/Makefile.am79
-rwxr-xr-xlibntfs-3g/Makefile.in841
-rwxr-xr-xlibntfs-3g/acls.c4296
-rwxr-xr-xlibntfs-3g/attrib.c5913
-rwxr-xr-xlibntfs-3g/attrlist.c314
-rwxr-xr-xlibntfs-3g/bitmap.c300
-rwxr-xr-xlibntfs-3g/bootsect.c285
-rwxr-xr-xlibntfs-3g/cache.c609
-rwxr-xr-xlibntfs-3g/collate.c271
-rwxr-xr-xlibntfs-3g/compat.c250
-rwxr-xr-xlibntfs-3g/compress.c1431
-rwxr-xr-xlibntfs-3g/debug.c79
-rwxr-xr-xlibntfs-3g/device.c730
-rwxr-xr-xlibntfs-3g/dir.c2582
-rwxr-xr-xlibntfs-3g/efs.c346
-rwxr-xr-xlibntfs-3g/index.c2063
-rwxr-xr-xlibntfs-3g/inode.c1566
-rwxr-xr-xlibntfs-3g/lcnalloc.c735
-rwxr-xr-xlibntfs-3g/libntfs-3g.pc.in10
-rwxr-xr-xlibntfs-3g/libntfs-3g.script.so.in2
-rwxr-xr-xlibntfs-3g/logfile.c737
-rwxr-xr-xlibntfs-3g/logging.c613
-rwxr-xr-xlibntfs-3g/mft.c1909
-rwxr-xr-xlibntfs-3g/misc.c61
-rwxr-xr-xlibntfs-3g/mst.c231
-rwxr-xr-xlibntfs-3g/object_id.c637
-rwxr-xr-xlibntfs-3g/reparse.c1222
-rwxr-xr-xlibntfs-3g/runlist.c2166
-rwxr-xr-xlibntfs-3g/security.c5167
-rwxr-xr-xlibntfs-3g/unistr.c1321
-rwxr-xr-xlibntfs-3g/unix_io.c386
-rwxr-xr-xlibntfs-3g/volume.c1663
-rwxr-xr-xlibntfs-3g/win32_io.c1477
-rwxr-xr-xlibtool7621
-rwxr-xr-xltmain.sh6956
-rwxr-xr-xm4/.keep0
-rwxr-xr-xmissing367
-rwxr-xr-xprog.IAB1071
-rwxr-xr-xprog.IAD5
-rwxr-xr-xprog.IMB466
-rwxr-xr-xprog.IMD2
-rwxr-xr-xprog.PFI2
-rwxr-xr-xprog.PO1
-rwxr-xr-xprog.PR14
-rwxr-xr-xprog.PRI219
-rwxr-xr-xprog.PS979
-rwxr-xr-xprog.SearchResults3
-rwxr-xr-xprog.WK39
-rwxr-xr-xsrc/lowntfs-3g.c4492
-rwxr-xr-xsrc/ntfs-3g.8.in349
-rwxr-xr-xsrc/ntfs-3g.c4461
-rwxr-xr-xsrc/ntfs-3g.probe.8.in81
-rwxr-xr-xsrc/ntfs-3g.probe.c163
-rwxr-xr-xsrc/ntfs-3g.secaudit.8.in171
-rwxr-xr-xsrc/ntfs-3g.usermap.8.in96
-rwxr-xr-xsrc/secaudit.c7176
-rwxr-xr-xsrc/secaudit.h731
-rwxr-xr-xsrc/test.c88
-rwxr-xr-xsrc/usermap.c1356
-rwxr-xr-xstamp-h11
148 files changed, 141286 insertions, 0 deletions
diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c
new file mode 100755
index 0000000..9c82765
--- a/dev/null
+++ b/libntfs-3g/dir.c
@@ -0,0 +1,2582 @@
+/**
+ * dir.c - Directory handling code. Originated from the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2005 Anton Altaparmakov
+ * Copyright (c) 2004-2005 Richard Russon
+ * Copyright (c) 2004-2008 Szabolcs Szakacsits
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2008-2010 Jean-Pierre Andre
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (in the main directory of the NTFS-3G
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+
+#include "param.h"
+#include "types.h"
+#include "debug.h"
+#include "attrib.h"
+#include "inode.h"
+#include "dir.h"
+#include "volume.h"
+#include "mft.h"
+#include "index.h"
+#include "ntfstime.h"
+#include "lcnalloc.h"
+#include "logging.h"
+#include "cache.h"
+#include "misc.h"
+#include "security.h"
+#include "reparse.h"
+#include "object_id.h"
+
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+/*
+ * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O"
+ * and "$Q" as global constants.
+ */
+ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'),
+ const_cpu_to_le16('3'), const_cpu_to_le16('0'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'),
+ const_cpu_to_le16('I'), const_cpu_to_le16('I'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'),
+ const_cpu_to_le16('D'), const_cpu_to_le16('H'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'),
+ const_cpu_to_le16('\0') };
+
+#if CACHE_INODE_SIZE
+
+/*
+ * Pathname hashing
+ *
+ * Based on first char and second char (which may be '\0')
+ */
+
+int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached)
+{
+ const char *path;
+ const unsigned char *name;
+
+ path = (const char*)cached->variable;
+ if (!path) {
+ ntfs_log_error("Bad inode cache entry\n");
+ return (-1);
+ }
+ name = (const unsigned char*)strrchr(path,'/');
+ if (!name)
+ name = (const unsigned char*)path;
+ return (((name[0] << 1) + name[1] + strlen((const char*)name))
+ % (2*CACHE_INODE_SIZE));
+}
+
+/*
+ * Pathname comparing for entering/fetching from cache
+ */
+
+static int inode_cache_compare(const struct CACHED_GENERIC *cached,
+ const struct CACHED_GENERIC *wanted)
+{
+ return (!cached->variable
+ || strcmp(cached->variable, wanted->variable));
+}
+
+/*
+ * Pathname comparing for invalidating entries in cache
+ *
+ * A partial path is compared in order to invalidate all paths
+ * related to a renamed directory
+ * inode numbers are also checked, as deleting a long name may
+ * imply deleting a short name and conversely
+ *
+ * Only use associated with a CACHE_NOHASH flag
+ */
+
+static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached,
+ const struct CACHED_GENERIC *wanted)
+{
+ int len;
+ BOOL different;
+ const struct CACHED_INODE *w;
+ const struct CACHED_INODE *c;
+
+ w = (const struct CACHED_INODE*)wanted;
+ c = (const struct CACHED_INODE*)cached;
+ if (w->pathname) {
+ len = strlen(w->pathname);
+ different = !cached->variable
+ || ((w->inum != MREF(c->inum))
+ && (strncmp(c->pathname, w->pathname, len)
+ || ((c->pathname[len] != '\0')
+ && (c->pathname[len] != '/'))));
+ } else
+ different = !c->pathname
+ || (w->inum != MREF(c->inum));
+ return (different);
+}
+
+#endif
+
+#if CACHE_LOOKUP_SIZE
+
+/*
+ * File name comparing for entering/fetching from lookup cache
+ */
+
+static int lookup_cache_compare(const struct CACHED_GENERIC *cached,
+ const struct CACHED_GENERIC *wanted)
+{
+ const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached;
+ const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted;
+ return (!c->name
+ || (c->parent != w->parent)
+ || (c->namesize != w->namesize)
+ || memcmp(c->name, w->name, c->namesize));
+}
+
+/*
+ * Inode number comparing for invalidating lookup cache
+ *
+ * All entries with designated inode number are invalidated
+ *
+ * Only use associated with a CACHE_NOHASH flag
+ */
+
+static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached,
+ const struct CACHED_GENERIC *wanted)
+{
+ const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached;
+ const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted;
+ return (!c->name
+ || (c->parent != w->parent)
+ || (MREF(c->inum) != MREF(w->inum)));
+}
+
+/*
+ * Lookup hashing
+ *
+ * Based on first, second and and last char
+ */
+
+int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached)
+{
+ const unsigned char *name;
+ int count;
+ unsigned int val;
+
+ name = (const unsigned char*)cached->variable;
+ count = cached->varsize;
+ if (!name || !count) {
+ ntfs_log_error("Bad lookup cache entry\n");
+ return (-1);
+ }
+ val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count;
+ return (val % (2*CACHE_LOOKUP_SIZE));
+}
+
+#endif
+
+/**
+ * ntfs_inode_lookup_by_name - find an inode in a directory given its name
+ * @dir_ni: ntfs inode of the directory in which to search for the name
+ * @uname: Unicode name for which to search in the directory
+ * @uname_len: length of the name @uname in Unicode characters
+ *
+ * Look for an inode with name @uname in the directory with inode @dir_ni.
+ * ntfs_inode_lookup_by_name() walks the contents of the directory looking for
+ * the Unicode name. If the name is found in the directory, the corresponding
+ * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it
+ * is a 64-bit number containing the sequence number.
+ *
+ * On error, return -1 with errno set to the error code. If the inode is is not
+ * found errno is ENOENT.
+ *
+ * Note, @uname_len does not include the (optional) terminating NULL character.
+ *
+ * Note, we look for a case sensitive match first but we also look for a case
+ * insensitive match at the same time. If we find a case insensitive match, we
+ * save that for the case that we don't find an exact match, where we return
+ * the mft reference of the case insensitive match.
+ *
+ * If the volume is mounted with the case sensitive flag set, then we only
+ * allow exact matches.
+ */
+u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
+ const ntfschar *uname, const int uname_len)
+{
+ VCN vcn;
+ u64 mref = 0;
+ s64 br;
+ ntfs_volume *vol = dir_ni->vol;
+ ntfs_attr_search_ctx *ctx;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia;
+ u8 *index_end;
+ ntfs_attr *ia_na;
+ int eo, rc;
+ u32 index_block_size, index_vcn_size;
+ u8 index_vcn_size_bits;
+
+ ntfs_log_trace("Entering\n");
+
+ if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ctx = ntfs_attr_get_search_ctx(dir_ni, NULL);
+ if (!ctx)
+ return -1;
+
+ /* Find the index root attribute in the mft record. */
+ 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 "
+ "%lld", (unsigned long long)dir_ni->mft_no);
+ goto put_err_out;
+ }
+ /* Get to the index root value. */
+ ir = (INDEX_ROOT*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+ 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_error("Index block size %u is invalid.\n",
+ (unsigned)index_block_size);
+ goto put_err_out;
+ }
+ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ir->index +
+ le32_to_cpu(ir->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ 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.
+ */
+ if (ie->ie_flags & INDEX_ENTRY_END)
+ break;
+
+ 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.
+ */
+ 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);
+ /*
+ * If uname collates before the name of the current entry, there
+ * is definitely no such name in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /* The names are not equal, continue the search. */
+ if (rc)
+ continue;
+ /*
+ * Perfect match, this will never happen as the
+ * ntfs_are_names_equal() call will have gotten a match but we
+ * still treat it correctly.
+ */
+ mref = le64_to_cpu(ie->indexed_file);
+ ntfs_attr_put_search_ctx(ctx);
+ return mref;
+ }
+ /*
+ * We have finished with this index without success. Check for the
+ * presence of a child node and if not present return error code
+ * ENOENT, unless we have got the mft reference of a matching name
+ * cached in mref in which case return mref.
+ */
+ if (!(ie->ie_flags & INDEX_ENTRY_NODE)) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (mref)
+ return mref;
+ ntfs_log_debug("Entry not found.\n");
+ errno = ENOENT;
+ return -1;
+ } /* Child node present, descend into it. */
+
+ /* 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 (inode %lld)",
+ (unsigned long long)dir_ni->mft_no);
+ goto put_err_out;
+ }
+
+ /* Allocate a buffer for the current index block. */
+ ia = ntfs_malloc(index_block_size);
+ if (!ia) {
+ ntfs_attr_close(ia_na);
+ goto put_err_out;
+ }
+
+ /* Determine the size of a vcn in the directory index. */
+ if (vol->cluster_size <= index_block_size) {
+ index_vcn_size = vol->cluster_size;
+ index_vcn_size_bits = vol->cluster_size_bits;
+ } else {
+ index_vcn_size = vol->sector_size;
+ index_vcn_size_bits = vol->sector_size_bits;
+ }
+
+ /* Get the starting vcn of the index_block holding the child node. */
+ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
+
+descend_into_child_node:
+
+ /* Read the index block starting at vcn. */
+ br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1,
+ index_block_size, ia);
+ if (br != 1) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read vcn 0x%llx",
+ (unsigned long long)vcn);
+ goto close_err_out;
+ }
+
+ if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
+ 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);
+ errno = EIO;
+ goto close_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 "
+ "has a size (%u) differing from the directory "
+ "specified size (%u).\n", (long long)vcn,
+ (unsigned long long)dir_ni->mft_no,
+ (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18,
+ (unsigned)index_block_size);
+ errno = EIO;
+ goto close_err_out;
+ }
+ 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",
+ (long long)vcn, (unsigned long long)dir_ni->mft_no);
+ errno = EIO;
+ goto close_err_out;
+ }
+
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * Iterate similar to above big loop but applied to index buffer, thus
+ * loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds check. */
+ if ((u8*)ie < (u8*)ia || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end) {
+ 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;
+ }
+ /*
+ * 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.
+ */
+ if (ie->ie_flags & INDEX_ENTRY_END)
+ break;
+
+ 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;
+ }
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ 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);
+ /*
+ * If uname collates before the name of the current entry, there
+ * is definitely no such name in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /* The names are not equal, continue the search. */
+ if (rc)
+ continue;
+ mref = le64_to_cpu(ie->indexed_file);
+ free(ia);
+ ntfs_attr_close(ia_na);
+ ntfs_attr_put_search_ctx(ctx);
+ return mref;
+ }
+ /*
+ * We have finished with this index buffer without success. Check for
+ * the presence of a child node.
+ */
+ if (ie->ie_flags & INDEX_ENTRY_NODE) {
+ if ((ia->index.ih_flags & NODE_MASK) == LEAF_NODE) {
+ 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;
+ }
+ /* Child node present, descend into it. */
+ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
+ if (vcn >= 0)
+ goto descend_into_child_node;
+ 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;
+ }
+ free(ia);
+ ntfs_attr_close(ia_na);
+ ntfs_attr_put_search_ctx(ctx);
+ /*
+ * No child node present, return error code ENOENT, unless we have got
+ * the mft reference of a matching name cached in mref in which case
+ * return mref.
+ */
+ if (mref)
+ return mref;
+ ntfs_log_debug("Entry not found.\n");
+ errno = ENOENT;
+ return -1;
+put_err_out:
+ eo = EIO;
+ ntfs_log_debug("Corrupt directory. Aborting lookup.\n");
+eo_put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = eo;
+ return -1;
+close_err_out:
+ eo = errno;
+ free(ia);
+ ntfs_attr_close(ia_na);
+ goto eo_put_err_out;
+}
+
+/*
+ * Lookup a file in a directory from its UTF-8 name
+ *
+ * The name is first fetched from cache if one is defined
+ *
+ * Returns the inode number
+ * or -1 if not possible (errno tells why)
+ */
+
+u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name)
+{
+ int uname_len;
+ ntfschar *uname = (ntfschar*)NULL;
+ u64 inum;
+#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;
+ /* enter into cache, even if not found */
+ ntfs_enter_cache(dir_ni->vol->lookup_cache,
+ GENERIC(&item),
+ lookup_cache_compare);
+ free(uname);
+ } else
+ inum = (s64)-1;
+ }
+ } 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;
+ }
+ return (inum);
+}
+
+/*
+ * Update a cache lookup record when a name has been defined
+ *
+ * The UTF-8 name is required
+ */
+
+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;
+
+ 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(
+ dir_ni->vol->lookup_cache,
+ GENERIC(&item), lookup_cache_compare);
+ if (cached)
+ cached->inum = inum;
+ }
+#endif
+}
+
+/**
+ * ntfs_pathname_to_inode - Find the inode which represents the given pathname
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @parent: A directory inode to begin the search (may be NULL)
+ * @pathname: Pathname to be located
+ *
+ * Take an ASCII pathname and find the inode that represents it. The function
+ * splits the path and then descends the directory tree. If @parent is NULL,
+ * then the root directory '.' will be used as the base for the search.
+ *
+ * Return: inode Success, the pathname was valid
+ * NULL Error, the pathname was invalid, or some other error occurred
+ */
+ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
+ const char *pathname)
+{
+ u64 inum;
+ int len, err = 0;
+ char *p, *q;
+ ntfs_inode *ni;
+ ntfs_inode *result = NULL;
+ ntfschar *unicode = NULL;
+ char *ascii = NULL;
+#if CACHE_INODE_SIZE
+ struct CACHED_INODE item;
+ struct CACHED_INODE *cached;
+ char *fullname;
+#endif
+
+ if (!vol || !pathname) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ntfs_log_trace("path: '%s'\n", pathname);
+
+ ascii = strdup(pathname);
+ if (!ascii) {
+ ntfs_log_error("Out of memory.\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ p = ascii;
+ /* Remove leading /'s. */
+ while (p && *p && *p == PATH_SEP)
+ p++;
+#if CACHE_INODE_SIZE
+ fullname = p;
+ if (p[0] && (p[strlen(p)-1] == PATH_SEP))
+ ntfs_log_error("Unnormalized path %s\n",ascii);
+#endif
+ if (parent) {
+ ni = parent;
+ } else {
+#if CACHE_INODE_SIZE
+ /*
+ * fetch inode for full path from cache
+ */
+ if (*fullname) {
+ item.pathname = fullname;
+ item.varsize = strlen(fullname) + 1;
+ cached = (struct CACHED_INODE*)ntfs_fetch_cache(
+ vol->xinode_cache, GENERIC(&item),
+ inode_cache_compare);
+ } else
+ cached = (struct CACHED_INODE*)NULL;
+ if (cached) {
+ /*
+ * return opened inode if found in cache
+ */
+ inum = MREF(cached->inum);
+ ni = ntfs_inode_open(vol, inum);
+ if (!ni) {
+ ntfs_log_debug("Cannot open inode %llu: %s.\n",
+ (unsigned long long)inum, p);
+ err = EIO;
+ }
+ result = ni;
+ goto out;
+ }
+#endif
+ ni = ntfs_inode_open(vol, FILE_root);
+ if (!ni) {
+ ntfs_log_debug("Couldn't open the inode of the root "
+ "directory.\n");
+ err = EIO;
+ result = (ntfs_inode*)NULL;
+ goto out;
+ }
+ }
+
+ while (p && *p) {
+ /* Find the end of the first token. */
+ q = strchr(p, PATH_SEP);
+ if (q != NULL) {
+ *q = '\0';
+ }
+#if CACHE_INODE_SIZE
+ /*
+ * fetch inode for partial path from cache
+ */
+ cached = (struct CACHED_INODE*)NULL;
+ if (!parent) {
+ item.pathname = fullname;
+ item.varsize = strlen(fullname) + 1;
+ cached = (struct CACHED_INODE*)ntfs_fetch_cache(
+ vol->xinode_cache, GENERIC(&item),
+ inode_cache_compare);
+ if (cached) {
+ inum = cached->inum;
+ }
+ }
+ /*
+ * if not in cache, translate, search, then
+ * insert into cache if found
+ */
+ if (!cached) {
+ len = ntfs_mbstoucs(p, &unicode);
+ if (len < 0) {
+ ntfs_log_perror("Could not convert filename to Unicode:"
+ " '%s'", p);
+ err = errno;
+ goto close;
+ } else if (len > NTFS_MAX_NAME_LEN) {
+ err = ENAMETOOLONG;
+ goto close;
+ }
+ inum = ntfs_inode_lookup_by_name(ni, unicode, len);
+ if (!parent && (inum != (u64) -1)) {
+ item.inum = inum;
+ ntfs_enter_cache(vol->xinode_cache,
+ GENERIC(&item),
+ inode_cache_compare);
+ }
+ }
+#else
+ len = ntfs_mbstoucs(p, &unicode);
+ if (len < 0) {
+ ntfs_log_perror("Could not convert filename to Unicode:"
+ " '%s'", p);
+ err = errno;
+ goto close;
+ } else if (len > NTFS_MAX_NAME_LEN) {
+ err = ENAMETOOLONG;
+ goto close;
+ }
+ inum = ntfs_inode_lookup_by_name(ni, unicode, len);
+#endif
+ if (inum == (u64) -1) {
+ ntfs_log_debug("Couldn't find name '%s' in pathname "
+ "'%s'.\n", p, pathname);
+ err = ENOENT;
+ goto close;
+ }
+
+ if (ni != parent)
+ if (ntfs_inode_close(ni)) {
+ err = errno;
+ goto out;
+ }
+
+ inum = MREF(inum);
+ ni = ntfs_inode_open(vol, inum);
+ if (!ni) {
+ ntfs_log_debug("Cannot open inode %llu: %s.\n",
+ (unsigned long long)inum, p);
+ err = EIO;
+ goto close;
+ }
+
+ free(unicode);
+ unicode = NULL;
+
+ if (q) *q++ = PATH_SEP; /* JPA */
+ p = q;
+ while (p && *p && *p == PATH_SEP)
+ p++;
+ }
+
+ result = ni;
+ ni = NULL;
+close:
+ if (ni && (ni != parent))
+ if (ntfs_inode_close(ni) && !err)
+ err = errno;
+out:
+ free(ascii);
+ free(unicode);
+ if (err)
+ errno = err;
+ return result;
+}
+
+/*
+ * The little endian Unicode string ".." for ntfs_readdir().
+ */
+static const ntfschar dotdot[3] = { const_cpu_to_le16('.'),
+ const_cpu_to_le16('.'),
+ const_cpu_to_le16('\0') };
+
+/*
+ * union index_union -
+ * More helpers for ntfs_readdir().
+ */
+typedef union {
+ INDEX_ROOT *ir;
+ INDEX_ALLOCATION *ia;
+} index_union __attribute__((__transparent_union__));
+
+/**
+ * enum INDEX_TYPE -
+ * More helpers for ntfs_readdir().
+ */
+typedef enum {
+ INDEX_TYPE_ROOT, /* index root */
+ INDEX_TYPE_ALLOCATION, /* index allocation */
+} INDEX_TYPE;
+
+/**
+ * ntfs_filldir - ntfs specific filldir method
+ * @dir_ni: ntfs inode of current directory
+ * @pos: current position in directory
+ * @ivcn_bits: log(2) of index vcn size
+ * @index_type: specifies whether @iu is an index root or an index allocation
+ * @iu: index root or index block to which @ie belongs
+ * @ie: current index entry
+ * @dirent: context for filldir callback supplied by the caller
+ * @filldir: filldir callback supplied by the caller
+ *
+ * Pass information specifying the current directory entry @ie to the @filldir
+ * callback.
+ */
+static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits,
+ const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie,
+ void *dirent, ntfs_filldir_t filldir)
+{
+ FILE_NAME_ATTR *fn = &ie->key.file_name;
+ unsigned dt_type;
+
+ ntfs_log_trace("Entering.\n");
+
+ /* Advance the position even if going to skip the entry. */
+ if (index_type == INDEX_TYPE_ALLOCATION)
+ *pos = (u8*)ie - (u8*)iu.ia + (sle64_to_cpu(
+ iu.ia->index_block_vcn) << ivcn_bits) +
+ dir_ni->vol->mft_record_size;
+ else /* if (index_type == INDEX_TYPE_ROOT) */
+ *pos = (u8*)ie - (u8*)iu.ir;
+ /* Skip root directory self reference entry. */
+ if (MREF_LE(ie->indexed_file) == FILE_root)
+ return 0;
+ if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT)
+ dt_type = NTFS_DT_DIR;
+ else if (fn->file_attributes & FILE_ATTR_SYSTEM)
+ dt_type = NTFS_DT_UNKNOWN;
+ else
+ dt_type = NTFS_DT_REG;
+ return filldir(dirent, fn->file_name, fn->file_name_length,
+ fn->file_name_type, *pos,
+ le64_to_cpu(ie->indexed_file), dt_type);
+}
+
+/**
+ * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode
+ * @ni: ntfs inode whose parent directory to find
+ *
+ * Find the parent directory of the ntfs inode @ni. To do this, find the first
+ * file name attribute in the mft record of @ni and return the parent mft
+ * reference from that.
+ *
+ * Note this only makes sense for directories, since files can be hard linked
+ * from multiple directories and there is no way for us to tell which one is
+ * being looked for.
+ *
+ * Technically directories can have hard links, too, but we consider that as
+ * illegal as Linux/UNIX do not support directory hard links.
+ *
+ * Return the mft reference of the parent directory on success or -1 on error
+ * with errno set to the error code.
+ */
+static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
+{
+ MFT_REF mref;
+ ntfs_attr_search_ctx *ctx;
+ FILE_NAME_ATTR *fn;
+ int eo;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni) {
+ errno = EINVAL;
+ return ERR_MREF(-1);
+ }
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return ERR_MREF(-1);
+ if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
+ 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_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_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);
+ ntfs_attr_put_search_ctx(ctx);
+ return mref;
+io_err_out:
+ errno = EIO;
+err_out:
+ eo = errno;
+ ntfs_attr_put_search_ctx(ctx);
+ errno = eo;
+ return ERR_MREF(-1);
+}
+
+/**
+ * ntfs_readdir - read the contents of an ntfs directory
+ * @dir_ni: ntfs inode of current directory
+ * @pos: current position in directory
+ * @dirent: context for filldir callback supplied by the caller
+ * @filldir: filldir callback supplied by the caller
+ *
+ * Parse the index root and the index blocks that are marked in use in the
+ * index bitmap and hand each found directory entry to the @filldir callback
+ * supplied by the caller.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ *
+ * Note: Index blocks are parsed in ascending vcn order, from which follows
+ * that the directory entries are not returned sorted.
+ */
+int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
+ void *dirent, ntfs_filldir_t filldir)
+{
+ s64 i_size, br, ia_pos, bmp_pos, ia_start;
+ ntfs_volume *vol;
+ ntfs_attr *ia_na, *bmp_na = NULL;
+ ntfs_attr_search_ctx *ctx = NULL;
+ u8 *index_end, *bmp = NULL;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia = NULL;
+ int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo;
+ u32 index_block_size, index_vcn_size;
+ u8 index_block_size_bits, index_vcn_size_bits;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!dir_ni || !pos || !filldir) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ vol = dir_ni->vol;
+
+ 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. */
+ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
+ if (!ia_na) {
+ if (errno != ENOENT) {
+ ntfs_log_perror("Failed to open index allocation attribute. "
+ "Directory inode %lld is corrupt or bug",
+ (unsigned long long)dir_ni->mft_no);
+ return -1;
+ }
+ i_size = 0;
+ } else
+ i_size = ia_na->data_size;
+
+ rc = 0;
+
+ /* Are we at end of dir yet? */
+ if (*pos >= i_size + vol->mft_record_size)
+ goto done;
+
+ /* Emulate . and .. for all directories. */
+ if (!*pos) {
+ rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos,
+ MK_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number)),
+ NTFS_DT_DIR);
+ if (rc)
+ goto err_out;
+ ++*pos;
+ }
+ if (*pos == 1) {
+ MFT_REF parent_mref;
+
+ parent_mref = ntfs_mft_get_parent_ref(dir_ni);
+ if (parent_mref == ERR_MREF(-1)) {
+ ntfs_log_perror("Parent directory not found");
+ goto dir_err_out;
+ }
+
+ rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos,
+ parent_mref, NTFS_DT_DIR);
+ if (rc)
+ goto err_out;
+ ++*pos;
+ }
+
+ ctx = ntfs_attr_get_search_ctx(dir_ni, NULL);
+ if (!ctx)
+ goto err_out;
+
+ /* Get the offset into the index root attribute. */
+ ir_pos = (int)*pos;
+ /* Find the index root attribute in the mft record. */
+ 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 "
+ "%lld", (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* Get to the index root value. */
+ ir = (INDEX_ROOT*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+
+ /* Determine the size of a vcn in the directory index. */
+ 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_error("Index block size %u is invalid.\n",
+ (unsigned)index_block_size);
+ goto dir_err_out;
+ }
+ index_block_size_bits = ffs(index_block_size) - 1;
+ if (vol->cluster_size <= index_block_size) {
+ index_vcn_size = vol->cluster_size;
+ index_vcn_size_bits = vol->cluster_size_bits;
+ } else {
+ index_vcn_size = vol->sector_size;
+ index_vcn_size_bits = vol->sector_size_bits;
+ }
+
+ /* Are we jumping straight into the index allocation attribute? */
+ if (*pos >= vol->mft_record_size) {
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ goto skip_index_root;
+ }
+
+ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ir->index +
+ le32_to_cpu(ir->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry or until filldir tells us it has had enough
+ * or signals an error (both covered by the rc test).
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ ntfs_log_debug("In index root, offset %d.\n", (int)((u8*)ie - (u8*)ir));
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end)
+ goto dir_err_out;
+ /* The last entry cannot contain a name. */
+ if (ie->ie_flags & INDEX_ENTRY_END)
+ break;
+
+ if (!le16_to_cpu(ie->length))
+ goto dir_err_out;
+
+ /* Skip index root entry if continuing previous readdir. */
+ if (ir_pos > (u8*)ie - (u8*)ir)
+ continue;
+ /*
+ * Submit the directory entry to ntfs_filldir(), which will
+ * invoke the filldir() callback as appropriate.
+ */
+ rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits,
+ INDEX_TYPE_ROOT, ir, ie, dirent, filldir);
+ if (rc) {
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ goto err_out;
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+
+ /* If there is no index allocation attribute we are finished. */
+ if (!ia_na)
+ goto EOD;
+
+ /* Advance *pos to the beginning of the index allocation. */
+ *pos = vol->mft_record_size;
+
+skip_index_root:
+
+ if (!ia_na)
+ goto done;
+
+ /* Allocate a buffer for the current index block. */
+ ia = ntfs_malloc(index_block_size);
+ if (!ia)
+ goto err_out;
+
+ bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4);
+ if (!bmp_na) {
+ ntfs_log_perror("Failed to open index bitmap attribute");
+ goto dir_err_out;
+ }
+
+ /* Get the offset into the index allocation attribute. */
+ ia_pos = *pos - vol->mft_record_size;
+
+ bmp_pos = ia_pos >> index_block_size_bits;
+ if (bmp_pos >> 3 >= bmp_na->data_size) {
+ ntfs_log_error("Current index position exceeds index bitmap "
+ "size.\n");
+ goto dir_err_out;
+ }
+
+ bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096);
+ bmp = ntfs_malloc(bmp_buf_size);
+ if (!bmp)
+ goto err_out;
+
+ br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
+ if (br != bmp_buf_size) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read from index bitmap attribute");
+ goto err_out;
+ }
+
+ bmp_buf_pos = 0;
+ /* If the index block is not in use find the next one that is. */
+ while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) {
+find_next_index_buffer:
+ bmp_pos++;
+ bmp_buf_pos++;
+ /* If we have reached the end of the bitmap, we are done. */
+ if (bmp_pos >> 3 >= bmp_na->data_size)
+ goto EOD;
+ ia_pos = bmp_pos << index_block_size_bits;
+ if (bmp_buf_pos >> 3 < bmp_buf_size)
+ continue;
+ /* Read next chunk from the index bitmap. */
+ bmp_buf_pos = 0;
+ if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size)
+ bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3);
+ br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
+ if (br != bmp_buf_size) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read from index bitmap attribute");
+ goto err_out;
+ }
+ }
+
+ ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos);
+
+ /* Read the index block starting at bmp_pos. */
+ br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1,
+ index_block_size, ia);
+ if (br != 1) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read index block");
+ goto err_out;
+ }
+
+ ia_start = ia_pos & ~(s64)(index_block_size - 1);
+ if (sle64_to_cpu(ia->index_block_vcn) != ia_start >>
+ index_vcn_size_bits) {
+ ntfs_log_error("Actual VCN (0x%llx) of index buffer is different "
+ "from expected VCN (0x%llx) in inode 0x%llx.\n",
+ (long long)sle64_to_cpu(ia->index_block_vcn),
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no);
+ 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 %lld "
+ "has a size (%u) differing from the directory "
+ "specified size (%u).\n", (long long)ia_start >>
+ index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no,
+ (unsigned) le32_to_cpu(ia->index.allocated_size)
+ + 0x18, (unsigned)index_block_size);
+ goto dir_err_out;
+ }
+ 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 "
+ "%lld exceeds maximum size.\n",
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry or until ntfs_filldir tells us it has had
+ * enough or signals an error (both covered by the rc test).
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ ntfs_log_debug("In index allocation, offset 0x%llx.\n",
+ (long long)ia_start + ((u8*)ie - (u8*)ia));
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ia || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end) {
+ ntfs_log_error("Index entry out of bounds in directory inode "
+ "%lld.\n", (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* The last entry cannot contain a name. */
+ if (ie->ie_flags & INDEX_ENTRY_END)
+ break;
+
+ if (!le16_to_cpu(ie->length))
+ goto dir_err_out;
+
+ /* Skip index entry if continuing previous readdir. */
+ if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
+ continue;
+ /*
+ * Submit the directory entry to ntfs_filldir(), which will
+ * invoke the filldir() callback as appropriate.
+ */
+ rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits,
+ INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir);
+ if (rc)
+ goto err_out;
+ }
+ goto find_next_index_buffer;
+EOD:
+ /* We are finished, set *pos to EOD. */
+ *pos = i_size + vol->mft_record_size;
+done:
+ free(ia);
+ free(bmp);
+ if (bmp_na)
+ ntfs_attr_close(bmp_na);
+ if (ia_na)
+ ntfs_attr_close(ia_na);
+ ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos);
+ return 0;
+dir_err_out:
+ errno = EIO;
+err_out:
+ eo = errno;
+ ntfs_log_trace("failed.\n");
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ free(ia);
+ free(bmp);
+ if (bmp_na)
+ ntfs_attr_close(bmp_na);
+ if (ia_na)
+ ntfs_attr_close(ia_na);
+ errno = eo;
+ return -1;
+}
+
+
+/**
+ * __ntfs_create - create object on ntfs volume
+ * @dir_ni: ntfs inode for directory in which create new object
+ * @securid: id of inheritable security descriptor, 0 if none
+ * @name: unicode name of new object
+ * @name_len: length of the name in unicode characters
+ * @type: type of the object to create
+ * @dev: major and minor device numbers (obtained from makedev())
+ * @target: target in unicode (only for symlinks)
+ * @target_len: length of target in unicode characters
+ *
+ * Internal, use ntfs_create{,_device,_symlink} wrappers instead.
+ *
+ * @type can be:
+ * S_IFREG to create regular file
+ * S_IFDIR to create directory
+ * S_IFBLK to create block device
+ * S_IFCHR to create character device
+ * S_IFLNK to create symbolic link
+ * S_IFIFO to create FIFO
+ * S_IFSOCK to create socket
+ * other values are invalid.
+ *
+ * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value
+ * ignored.
+ *
+ * @target and @target_len are used only if @type is S_IFLNK, in other cases
+ * their value ignored.
+ *
+ * Return opened ntfs inode that describes created object on success or NULL
+ * on error with errno set to the error code.
+ */
+static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
+ ntfschar *name, u8 name_len, mode_t type, dev_t dev,
+ ntfschar *target, int target_len)
+{
+ ntfs_inode *ni;
+ int rollback_data = 0, rollback_sd = 0;
+ FILE_NAME_ATTR *fn = NULL;
+ STANDARD_INFORMATION *si = NULL;
+ int err, fn_len, si_len;
+
+ ntfs_log_trace("Entering.\n");
+
+ /* Sanity checks. */
+ if (!dir_ni || !name || !name_len) {
+ ntfs_log_error("Invalid arguments.\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) {
+ errno = EOPNOTSUPP;
+ return NULL;
+ }
+
+ ni = ntfs_mft_record_alloc(dir_ni->vol, NULL);
+ if (!ni)
+ return NULL;
+#if CACHE_NIDATA_SIZE
+ ntfs_inode_invalidate(dir_ni->vol, ni->mft_no);
+#endif
+ /*
+ * Create STANDARD_INFORMATION attribute.
+ * JPA Depending on available inherited security descriptor,
+ * Write STANDARD_INFORMATION v1.2 (no inheritance) or v3
+ */
+ if (securid)
+ si_len = sizeof(STANDARD_INFORMATION);
+ else
+ si_len = offsetof(STANDARD_INFORMATION, v1_end);
+ si = ntfs_calloc(si_len);
+ if (!si) {
+ err = errno;
+ goto err_out;
+ }
+ si->creation_time = ni->creation_time;
+ si->last_data_change_time = ni->last_data_change_time;
+ si->last_mft_change_time = ni->last_mft_change_time;
+ si->last_access_time = ni->last_access_time;
+ if (securid) {
+ set_nino_flag(ni, v3_Extensions);
+ ni->owner_id = si->owner_id = 0;
+ ni->security_id = si->security_id = securid;
+ ni->quota_charged = si->quota_charged = const_cpu_to_le64(0);
+ ni->usn = si->usn = const_cpu_to_le64(0);
+ } else
+ clear_nino_flag(ni, v3_Extensions);
+ if (!S_ISREG(type) && !S_ISDIR(type)) {
+ si->file_attributes = FILE_ATTR_SYSTEM;
+ ni->flags = FILE_ATTR_SYSTEM;
+ }
+ ni->flags |= FILE_ATTR_ARCHIVE;
+ if ((dir_ni->flags & FILE_ATTR_COMPRESSED)
+ && (S_ISREG(type) || S_ISDIR(type)))
+ ni->flags |= FILE_ATTR_COMPRESSED;
+ /* Add STANDARD_INFORMATION to inode. */
+ if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0,
+ (u8*)si, si_len)) {
+ err = errno;
+ ntfs_log_error("Failed to add STANDARD_INFORMATION "
+ "attribute.\n");
+ goto err_out;
+ }
+
+ if (!securid) {
+ if (ntfs_sd_add_everyone(ni)) {
+ err = errno;
+ goto err_out;
+ }
+ }
+ rollback_sd = 1;
+
+ if (S_ISDIR(type)) {
+ INDEX_ROOT *ir = NULL;
+ INDEX_ENTRY *ie;
+ int ir_len, index_len;
+
+ /* Create INDEX_ROOT attribute. */
+ index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER);
+ ir_len = offsetof(INDEX_ROOT, index) + index_len;
+ ir = ntfs_calloc(ir_len);
+ if (!ir) {
+ err = errno;
+ goto err_out;
+ }
+ ir->type = AT_FILE_NAME;
+ ir->collation_rule = COLLATION_FILE_NAME;
+ ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size);
+ if (ni->vol->cluster_size <= ni->vol->indx_record_size)
+ ir->clusters_per_index_block =
+ ni->vol->indx_record_size >>
+ ni->vol->cluster_size_bits;
+ else
+ ir->clusters_per_index_block =
+ ni->vol->indx_record_size >>
+ ni->vol->sector_size_bits;
+ ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER));
+ ir->index.index_length = cpu_to_le32(index_len);
+ ir->index.allocated_size = cpu_to_le32(index_len);
+ ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT));
+ ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER));
+ ie->key_length = 0;
+ ie->ie_flags = INDEX_ENTRY_END;
+ /* Add INDEX_ROOT attribute to inode. */
+ if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4,
+ (u8*)ir, ir_len)) {
+ err = errno;
+ free(ir);
+ ntfs_log_error("Failed to add INDEX_ROOT attribute.\n");
+ goto err_out;
+ }
+ free(ir);
+ } else {
+ INTX_FILE *data;
+ int data_len;
+
+ switch (type) {
+ case S_IFBLK:
+ case S_IFCHR:
+ data_len = offsetof(INTX_FILE, device_end);
+ data = ntfs_malloc(data_len);
+ if (!data) {
+ err = errno;
+ goto err_out;
+ }
+ data->major = cpu_to_le64(major(dev));
+ data->minor = cpu_to_le64(minor(dev));
+ if (type == S_IFBLK)
+ data->magic = INTX_BLOCK_DEVICE;
+ if (type == S_IFCHR)
+ data->magic = INTX_CHARACTER_DEVICE;
+ break;
+ case S_IFLNK:
+ data_len = sizeof(INTX_FILE_TYPES) +
+ target_len * sizeof(ntfschar);
+ data = ntfs_malloc(data_len);
+ if (!data) {
+ err = errno;
+ goto err_out;
+ }
+ data->magic = INTX_SYMBOLIC_LINK;
+ memcpy(data->target, target,
+ target_len * sizeof(ntfschar));
+ break;
+ case S_IFSOCK:
+ data = NULL;
+ data_len = 1;
+ break;
+ default: /* FIFO or regular file. */
+ data = NULL;
+ data_len = 0;
+ break;
+ }
+ /* Add DATA attribute to inode. */
+ if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data,
+ data_len)) {
+ err = errno;
+ ntfs_log_error("Failed to add DATA attribute.\n");
+ free(data);
+ goto err_out;
+ }
+ rollback_data = 1;
+ free(data);
+ }
+ /* Create FILE_NAME attribute. */
+ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
+ fn = ntfs_calloc(fn_len);
+ if (!fn) {
+ err = errno;
+ goto err_out;
+ }
+ fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number));
+ fn->file_name_length = name_len;
+ fn->file_name_type = FILE_NAME_POSIX;
+ if (S_ISDIR(type))
+ fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT;
+ if (!S_ISREG(type) && !S_ISDIR(type))
+ fn->file_attributes = FILE_ATTR_SYSTEM;
+ else
+ fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED;
+ fn->file_attributes |= FILE_ATTR_ARCHIVE;
+ fn->creation_time = ni->creation_time;
+ fn->last_data_change_time = ni->last_data_change_time;
+ fn->last_mft_change_time = ni->last_mft_change_time;
+ fn->last_access_time = ni->last_access_time;
+ if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
+ fn->data_size = fn->allocated_size = const_cpu_to_le64(0);
+ else {
+ fn->data_size = cpu_to_sle64(ni->data_size);
+ fn->allocated_size = cpu_to_sle64(ni->allocated_size);
+ }
+ memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
+ /* Add FILE_NAME attribute to inode. */
+ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
+ err = errno;
+ ntfs_log_error("Failed to add FILE_NAME attribute.\n");
+ goto err_out;
+ }
+ /* Add FILE_NAME attribute to index. */
+ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
+ le16_to_cpu(ni->mrec->sequence_number)))) {
+ err = errno;
+ ntfs_log_perror("Failed to add entry to the index");
+ goto err_out;
+ }
+ /* Set hard links count and directory flag. */
+ ni->mrec->link_count = cpu_to_le16(1);
+ if (S_ISDIR(type))
+ ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY;
+ ntfs_inode_mark_dirty(ni);
+ /* Done! */
+ free(fn);
+ free(si);
+ ntfs_log_trace("Done.\n");
+ return ni;
+err_out:
+ ntfs_log_trace("Failed.\n");
+
+ if (rollback_sd)
+ ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
+
+ if (rollback_data)
+ ntfs_attr_remove(ni, AT_DATA, AT_UNNAMED, 0);
+ /*
+ * Free extent MFT records (should not exist any with current
+ * ntfs_create implementation, but for any case if something will be
+ * changed in the future).
+ */
+ while (ni->nr_extents)
+ if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) {
+ err = errno;
+ ntfs_log_error("Failed to free extent MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+ if (ntfs_mft_record_free(ni->vol, ni))
+ ntfs_log_error("Failed to free MFT record. "
+ "Leaving inconsistent metadata. Run chkdsk.\n");
+ free(fn);
+ free(si);
+ errno = err;
+ return NULL;
+}
+
+/**
+ * Some wrappers around __ntfs_create() ...
+ */
+
+ntfs_inode *ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfschar *name,
+ u8 name_len, mode_t type)
+{
+ if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO &&
+ type != S_IFSOCK) {
+ ntfs_log_error("Invalid arguments.\n");
+ return NULL;
+ }
+ return __ntfs_create(dir_ni, securid, name, name_len, type, 0, NULL, 0);
+}
+
+ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, le32 securid,
+ ntfschar *name, u8 name_len, mode_t type, dev_t dev)
+{
+ if (type != S_IFCHR && type != S_IFBLK) {
+ ntfs_log_error("Invalid arguments.\n");
+ return NULL;
+ }
+ return __ntfs_create(dir_ni, securid, name, name_len, type, dev, NULL, 0);
+}
+
+ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, le32 securid,
+ ntfschar *name, u8 name_len, ntfschar *target, int target_len)
+{
+ if (!target || !target_len) {
+ ntfs_log_error("%s: Invalid argument (%p, %d)\n", __FUNCTION__,
+ target, target_len);
+ return NULL;
+ }
+ return __ntfs_create(dir_ni, securid, name, name_len, S_IFLNK, 0,
+ target, target_len);
+}
+
+int ntfs_check_empty_dir(ntfs_inode *ni)
+{
+ ntfs_attr *na;
+ int ret = 0;
+
+ if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))
+ return 0;
+
+ na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4);
+ if (!na) {
+ errno = EIO;
+ ntfs_log_perror("Failed to open directory");
+ return -1;
+ }
+
+ /* Non-empty directory? */
+ if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER))){
+ /* Both ENOTEMPTY and EEXIST are ok. We use the more common. */
+ errno = ENOTEMPTY;
+ ntfs_log_debug("Directory is not empty\n");
+ ret = -1;
+ }
+
+ ntfs_attr_close(na);
+ return ret;
+}
+
+static int ntfs_check_unlinkable_dir(ntfs_inode *ni, FILE_NAME_ATTR *fn)
+{
+ int link_count = le16_to_cpu(ni->mrec->link_count);
+ int ret;
+
+ ret = ntfs_check_empty_dir(ni);
+ if (!ret || errno != ENOTEMPTY)
+ return ret;
+ /*
+ * Directory is non-empty, so we can unlink only if there is more than
+ * one "real" hard link, i.e. links aren't different DOS and WIN32 names
+ */
+ if ((link_count == 1) ||
+ (link_count == 2 && fn->file_name_type == FILE_NAME_DOS)) {
+ errno = ENOTEMPTY;
+ ntfs_log_debug("Non-empty directory without hard links\n");
+ goto no_hardlink;
+ }
+
+ ret = 0;
+no_hardlink:
+ return ret;
+}
+
+/**
+ * ntfs_delete - delete file or directory from ntfs volume
+ * @ni: ntfs inode for object to delte
+ * @dir_ni: ntfs inode for directory in which delete object
+ * @name: unicode name of the object to delete
+ * @name_len: length of the name in unicode characters
+ *
+ * @ni is always closed after the call to this function (even if it failed),
+ * user does not need to call ntfs_inode_close himself.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_delete(ntfs_volume *vol, const char *pathname,
+ ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
+{
+ ntfs_attr_search_ctx *actx = NULL;
+ FILE_NAME_ATTR *fn = NULL;
+ BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
+ BOOL case_sensitive_match = TRUE;
+ int err = 0;
+#if CACHE_NIDATA_SIZE
+ int i;
+#endif
+#if CACHE_INODE_SIZE
+ struct CACHED_INODE item;
+ const char *p;
+ u64 inum = (u64)-1;
+ int count;
+#endif
+#if CACHE_LOOKUP_SIZE
+ struct CACHED_LOOKUP lkitem;
+#endif
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni || !dir_ni || !name || !name_len) {
+ ntfs_log_error("Invalid arguments.\n");
+ errno = EINVAL;
+ goto err_out;
+ }
+ if (ni->nr_extents == -1)
+ ni = ni->base_ni;
+ if (dir_ni->nr_extents == -1)
+ dir_ni = dir_ni->base_ni;
+ /*
+ * Search for FILE_NAME attribute with such name. If it's in POSIX or
+ * WIN32_AND_DOS namespace, then simply remove it from index and inode.
+ * If filename in DOS or in WIN32 namespace, then remove DOS name first,
+ * only then remove WIN32 name.
+ */
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx)
+ goto err_out;
+search:
+ while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
+ 0, NULL, 0, actx)) {
+ char *s;
+ BOOL case_sensitive = IGNORE_CASE;
+
+ errno = 0;
+ fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->value_offset));
+ s = ntfs_attr_name_get(fn->file_name, fn->file_name_length);
+ ntfs_log_trace("name: '%s' type: %d dos: %d win32: %d "
+ "case: %d\n", s, fn->file_name_type,
+ looking_for_dos_name, looking_for_win32_name,
+ case_sensitive_match);
+ ntfs_attr_name_free(&s);
+ if (looking_for_dos_name) {
+ if (fn->file_name_type == FILE_NAME_DOS)
+ break;
+ else
+ continue;
+ }
+ if (looking_for_win32_name) {
+ if (fn->file_name_type == FILE_NAME_WIN32)
+ break;
+ else
+ continue;
+ }
+
+ /* Ignore hard links from other directories */
+ if (dir_ni->mft_no != MREF_LE(fn->parent_directory)) {
+ ntfs_log_debug("MFT record numbers don't match "
+ "(%llu != %llu)\n",
+ (long long unsigned)dir_ni->mft_no,
+ (long long unsigned)MREF_LE(fn->parent_directory));
+ continue;
+ }
+
+ if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match)
+ case_sensitive = CASE_SENSITIVE;
+
+ if (ntfs_names_are_equal(fn->file_name, fn->file_name_length,
+ name, name_len, case_sensitive,
+ ni->vol->upcase, ni->vol->upcase_len)){
+
+ if (fn->file_name_type == FILE_NAME_WIN32) {
+ looking_for_dos_name = TRUE;
+ ntfs_attr_reinit_search_ctx(actx);
+ continue;
+ }
+ if (fn->file_name_type == FILE_NAME_DOS)
+ looking_for_dos_name = TRUE;
+ break;
+ }
+ }
+ if (errno) {
+ /*
+ * If case sensitive search failed, then try once again
+ * ignoring case.
+ */
+ if (errno == ENOENT && case_sensitive_match) {
+ case_sensitive_match = FALSE;
+ ntfs_attr_reinit_search_ctx(actx);
+ goto search;
+ }
+ goto err_out;
+ }
+
+ if (ntfs_check_unlinkable_dir(ni, fn) < 0)
+ goto err_out;
+
+ if (ntfs_index_remove(dir_ni, ni, fn, le32_to_cpu(actx->attr->value_length)))
+ goto err_out;
+
+ if (ntfs_attr_record_rm(actx))
+ goto err_out;
+
+ ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
+ ni->mrec->link_count) - 1);
+
+ ntfs_inode_mark_dirty(ni);
+ if (looking_for_dos_name) {
+ looking_for_dos_name = FALSE;
+ looking_for_win32_name = TRUE;
+ ntfs_attr_reinit_search_ctx(actx);
+ goto search;
+ }
+ /* TODO: Update object id, quota and securiry indexes if required. */
+ /*
+ * If hard link count is not equal to zero then we are done. In other
+ * case there are no reference to this inode left, so we should free all
+ * non-resident attributes and mark all MFT record as not in use.
+ */
+#if CACHE_LOOKUP_SIZE
+ /* invalidate entry in lookup cache */
+ lkitem.name = (const char*)NULL;
+ lkitem.namesize = 0;
+ lkitem.inum = ni->mft_no;
+ lkitem.parent = dir_ni->mft_no;
+ ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem),
+ lookup_cache_inv_compare, CACHE_NOHASH);
+#endif
+#if CACHE_INODE_SIZE
+ inum = ni->mft_no;
+ if (pathname) {
+ /* invalide cache entry, even if there was an error */
+ /* Remove leading /'s. */
+ p = pathname;
+ while (*p == PATH_SEP)
+ p++;
+ if (p[0] && (p[strlen(p)-1] == PATH_SEP))
+ ntfs_log_error("Unnormalized path %s\n",pathname);
+ item.pathname = p;
+ item.varsize = strlen(p);
+ } else {
+ item.pathname = (const char*)NULL;
+ item.varsize = 0;
+ }
+ item.inum = inum;
+ count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item),
+ inode_cache_inv_compare, CACHE_NOHASH);
+ if (pathname && !count)
+ ntfs_log_error("Could not delete inode cache entry for %s\n",
+ pathname);
+#endif
+ if (ni->mrec->link_count) {
+ ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME);
+ goto ok;
+ }
+ if (ntfs_delete_reparse_index(ni)) {
+ /*
+ * Failed to remove the reparse index : proceed anyway
+ * This is not a critical error, the entry is useless
+ * because of sequence_number, and stopping file deletion
+ * would be much worse as the file is not referenced now.
+ */
+ err = errno;
+ }
+ if (ntfs_delete_object_id_index(ni)) {
+ /*
+ * Failed to remove the object id index : proceed anyway
+ * This is not a critical error.
+ */
+ err = errno;
+ }
+ ntfs_attr_reinit_search_ctx(actx);
+ while (!ntfs_attrs_walk(actx)) {
+ if (actx->attr->non_resident) {
+ runlist *rl;
+
+ rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr,
+ NULL);
+ if (!rl) {
+ err = errno;
+ ntfs_log_error("Failed to decompress runlist. "
+ "Leaving inconsistent metadata.\n");
+ continue;
+ }
+ if (ntfs_cluster_free_from_rl(ni->vol, rl)) {
+ err = errno;
+ ntfs_log_error("Failed to free clusters. "
+ "Leaving inconsistent metadata.\n");
+ continue;
+ }
+ free(rl);
+ }
+ }
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_error("Attribute enumeration failed. "
+ "Probably leaving inconsistent metadata.\n");
+ }
+ /* All extents should be attached after attribute walk. */
+#if CACHE_NIDATA_SIZE
+ /*
+ * Disconnect extents before deleting them, so they are
+ * not wrongly moved to cache through the chainings
+ */
+ for (i=ni->nr_extents-1; i>=0; i--) {
+ ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL;
+ ni->extent_nis[i]->nr_extents = 0;
+ if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) {
+ err = errno;
+ ntfs_log_error("Failed to free extent MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+ }
+ free(ni->extent_nis);
+ ni->nr_extents = 0;
+ ni->extent_nis = (ntfs_inode**)NULL;
+#else
+ while (ni->nr_extents)
+ if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) {
+ err = errno;
+ ntfs_log_error("Failed to free extent MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+#endif
+ if (ntfs_mft_record_free(ni->vol, ni)) {
+ err = errno;
+ ntfs_log_error("Failed to free base MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+ ni = NULL;
+ok:
+ ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+out:
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ if (ntfs_inode_close(dir_ni) && !err)
+ err = errno;
+ if (ntfs_inode_close(ni) && !err)
+ err = errno;
+ if (err) {
+ errno = err;
+ ntfs_log_debug("Could not delete file: %s\n", strerror(errno));
+ return -1;
+ }
+ ntfs_log_trace("Done.\n");
+ return 0;
+err_out:
+ err = errno;
+ goto out;
+}
+
+/**
+ * ntfs_link - create hard link for file or directory
+ * @ni: ntfs inode for object to create hard link
+ * @dir_ni: ntfs inode for directory in which new link should be placed
+ * @name: unicode name of the new link
+ * @name_len: length of the name in unicode characters
+ *
+ * NOTE: At present we allow creating hardlinks to directories, we use them
+ * in a temporary state during rename. But it's defenitely bad idea to have
+ * hard links to directories as a result of operation.
+ * FIXME: Create internal __ntfs_link that allows hard links to a directories
+ * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
+ u8 name_len, FILE_NAME_TYPE_FLAGS nametype)
+{
+ FILE_NAME_ATTR *fn = NULL;
+ int fn_len, err;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni || !dir_ni || !name || !name_len ||
+ ni->mft_no == dir_ni->mft_no) {
+ err = EINVAL;
+ ntfs_log_perror("ntfs_link wrong arguments");
+ goto err_out;
+ }
+
+ if ((ni->flags & FILE_ATTR_REPARSE_POINT)
+ && !ntfs_possible_symlink(ni)) {
+ err = EOPNOTSUPP;
+ goto err_out;
+ }
+
+ /* Create FILE_NAME attribute. */
+ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
+ fn = ntfs_calloc(fn_len);
+ if (!fn) {
+ err = errno;
+ goto err_out;
+ }
+ fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number));
+ fn->file_name_length = name_len;
+ fn->file_name_type = nametype;
+ fn->file_attributes = ni->flags;
+ if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
+ fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT;
+ fn->data_size = fn->allocated_size = const_cpu_to_le64(0);
+ } else {
+ fn->allocated_size = cpu_to_sle64(ni->allocated_size);
+ fn->data_size = cpu_to_sle64(ni->data_size);
+ }
+ fn->creation_time = ni->creation_time;
+ fn->last_data_change_time = ni->last_data_change_time;
+ fn->last_mft_change_time = ni->last_mft_change_time;
+ fn->last_access_time = ni->last_access_time;
+ memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
+ /* Add FILE_NAME attribute to index. */
+ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
+ le16_to_cpu(ni->mrec->sequence_number)))) {
+ err = errno;
+ ntfs_log_perror("Failed to add filename to the index");
+ goto err_out;
+ }
+ /* Add FILE_NAME attribute to inode. */
+ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
+ ntfs_log_error("Failed to add FILE_NAME attribute.\n");
+ err = errno;
+ /* Try to remove just added attribute from index. */
+ if (ntfs_index_remove(dir_ni, ni, fn, fn_len))
+ goto rollback_failed;
+ goto err_out;
+ }
+ /* Increment hard links count. */
+ ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
+ ni->mrec->link_count) + 1);
+ /* Done! */
+ ntfs_inode_mark_dirty(ni);
+ free(fn);
+ ntfs_log_trace("Done.\n");
+ return 0;
+rollback_failed:
+ ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n");
+err_out:
+ free(fn);
+ errno = err;
+ return -1;
+}
+
+int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
+{
+ return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX));
+}
+
+/*
+ * Get a parent directory from an inode entry
+ *
+ * This is only used in situations where the path used to access
+ * the current file is not known for sure. The result may be different
+ * from the path when the file is linked in several parent directories.
+ *
+ * Currently this is only used for translating ".." in the target
+ * of a Vista relative symbolic link
+ */
+
+ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni)
+{
+ ntfs_inode *dir_ni = (ntfs_inode*)NULL;
+ u64 inum;
+ FILE_NAME_ATTR *fn;
+ ntfs_attr_search_ctx *ctx;
+
+ if (ni->mft_no != FILE_root) {
+ /* find the name in the attributes */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return ((ntfs_inode*)NULL);
+
+ if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0,
+ CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ /* We know this will always be resident. */
+ fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+ inum = le64_to_cpu(fn->parent_directory);
+ if (inum != (u64)-1) {
+ dir_ni = ntfs_inode_open(ni->vol, MREF(inum));
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ }
+ return (dir_ni);
+}
+
+#ifdef HAVE_SETXATTR
+
+#define MAX_DOS_NAME_LENGTH 12
+
+/*
+ * Get a DOS name for a file in designated directory
+ *
+ * Returns size if found
+ * 0 if not found
+ * -1 if there was an error (described by errno)
+ */
+
+static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname)
+{
+ size_t outsize = 0;
+ FILE_NAME_ATTR *fn;
+ ntfs_attr_search_ctx *ctx;
+
+ /* find the name in the attributes */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return -1;
+
+ while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ /* We know this will always be resident. */
+ fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+
+ if ((fn->file_name_type & FILE_NAME_DOS)
+ && (MREF_LE(fn->parent_directory) == dnum)) {
+ /*
+ * Found a DOS or WIN32+DOS name for the entry
+ * copy name, after truncation for safety
+ */
+ outsize = fn->file_name_length;
+/* TODO : reject if name is too long ? */
+ if (outsize > MAX_DOS_NAME_LENGTH)
+ outsize = MAX_DOS_NAME_LENGTH;
+ memcpy(dosname,fn->file_name,outsize*sizeof(ntfschar));
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ return (outsize);
+}
+
+
+/*
+ * Get a long name for a file in designated directory
+ *
+ * Returns size if found
+ * 0 if not found
+ * -1 if there was an error (described by errno)
+ */
+
+static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname)
+{
+ size_t outsize = 0;
+ FILE_NAME_ATTR *fn;
+ ntfs_attr_search_ctx *ctx;
+
+ /* find the name in the attributes */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return -1;
+
+ /* first search for WIN32 or DOS+WIN32 names */
+ while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ /* We know this will always be resident. */
+ fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+
+ if ((fn->file_name_type & FILE_NAME_WIN32)
+ && (MREF_LE(fn->parent_directory) == dnum)) {
+ /*
+ * Found a WIN32 or WIN32+DOS name for the entry
+ * copy name
+ */
+ outsize = fn->file_name_length;
+ memcpy(longname,fn->file_name,outsize*sizeof(ntfschar));
+ }
+ }
+ /* if not found search for POSIX names */
+ if (!outsize) {
+ ntfs_attr_reinit_search_ctx(ctx);
+ while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ /* We know this will always be resident. */
+ fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+
+ if ((fn->file_name_type == FILE_NAME_POSIX)
+ && (MREF_LE(fn->parent_directory) == dnum)) {
+ /*
+ * Found a POSIX name for the entry
+ * copy name
+ */
+ outsize = fn->file_name_length;
+ memcpy(longname,fn->file_name,outsize*sizeof(ntfschar));
+ }
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ return (outsize);
+}
+
+
+/*
+ * Get the ntfs DOS name into an extended attribute
+ */
+
+int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
+ char *value, size_t size)
+{
+ int outsize = 0;
+ char *outname = (char*)NULL;
+ u64 dnum;
+ int doslen;
+ ntfschar dosname[MAX_DOS_NAME_LENGTH];
+
+ dnum = dir_ni->mft_no;
+ doslen = get_dos_name(ni, dnum, dosname);
+ if (doslen > 0) {
+ /*
+ * Found a DOS name for the entry, make
+ * uppercase and encode into the buffer
+ * if there is enough space
+ */
+ ntfs_name_upcase(dosname, doslen,
+ ni->vol->upcase, ni->vol->upcase_len);
+ if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) {
+ ntfs_log_error("Cannot represent dosname in current locale.\n");
+ outsize = -errno;
+ } else {
+ outsize = strlen(outname);
+ if (value && (outsize <= (int)size))
+ memcpy(value, outname, outsize);
+ else
+ if (size && (outsize > (int)size))
+ outsize = -ERANGE;
+ free(outname);
+ }
+ } else {
+ if (doslen == 0)
+ errno = ENODATA;
+ outsize = -errno;
+ }
+ return (outsize);
+}
+
+/*
+ * Change the name space of an existing file or directory
+ *
+ * Returns the old namespace if successful
+ * -1 if an error occurred (described by errno)
+ */
+
+static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni,
+ ntfschar *name, int len,
+ FILE_NAME_TYPE_FLAGS nametype)
+{
+ ntfs_attr_search_ctx *actx;
+ ntfs_index_context *icx;
+ FILE_NAME_ATTR *fnx;
+ FILE_NAME_ATTR *fn = NULL;
+ BOOL found;
+ int lkup;
+ int ret;
+
+ ret = -1;
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (actx) {
+ found = FALSE;
+ do {
+ lkup = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0,
+ CASE_SENSITIVE, 0, NULL, 0, actx);
+ if (!lkup) {
+ fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->value_offset));
+ found = (MREF_LE(fn->parent_directory)
+ == dir_ni->mft_no)
+ && !memcmp(fn->file_name, name,
+ len*sizeof(ntfschar));
+ }
+ } while (!lkup && !found);
+ if (found) {
+ icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
+ if (icx) {
+ lkup = ntfs_index_lookup((char*)fn, len, icx);
+ if (!lkup && icx->data && icx->data_len) {
+ fnx = (FILE_NAME_ATTR*)icx->data;
+ ret = fn->file_name_type;
+ fn->file_name_type = nametype;
+ fnx->file_name_type = nametype;
+ ntfs_inode_mark_dirty(ni);
+ ntfs_index_entry_mark_dirty(icx);
+ }
+ ntfs_index_ctx_put(icx);
+ }
+ }
+ ntfs_attr_put_search_ctx(actx);
+ }
+ return (ret);
+}
+
+/*
+ * Set a DOS name to a file and adjust name spaces
+ *
+ * If the new names are collapsible (same uppercased chars) :
+ *
+ * - the existing DOS name or DOS+Win32 name is made Posix
+ * - if it was a real DOS name, the existing long name is made DOS+Win32
+ * and the existing DOS name is deleted
+ * - finally the existing long name is made DOS+Win32 unless already done
+ *
+ * If the new names are not collapsible :
+ *
+ * - insert the short name as a DOS name
+ * - delete the old long name or existing short name
+ * - insert the new long name (as a Win32 or DOS+Win32 name)
+ *
+ * Deleting the old long name will not delete the file
+ * provided the old name was in the Posix name space,
+ * because the alternate name has been set before.
+ *
+ * The inodes of file and parent directory are always closed
+ *
+ * Returns 0 if successful
+ * -1 if failed
+ */
+
+static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
+ ntfschar *shortname, int shortlen,
+ ntfschar *longname, int longlen,
+ ntfschar *deletename, int deletelen, BOOL existed)
+{
+ unsigned int linkcount;
+ ntfs_volume *vol;
+ BOOL collapsible;
+ BOOL deleted;
+ BOOL done;
+ FILE_NAME_TYPE_FLAGS oldnametype;
+ u64 dnum;
+ u64 fnum;
+ int res;
+
+ res = -1;
+ vol = ni->vol;
+ dnum = dir_ni->mft_no;
+ fnum = ni->mft_no;
+ /* save initial link count */
+ linkcount = le16_to_cpu(ni->mrec->link_count);
+
+ /* check whether the same name may be used as DOS and WIN32 */
+ collapsible = ntfs_collapsible_chars(ni->vol, shortname, shortlen,
+ longname, longlen);
+ if (collapsible) {
+ deleted = FALSE;
+ done = FALSE;
+ if (existed) {
+ oldnametype = set_namespace(ni, dir_ni, deletename,
+ deletelen, FILE_NAME_POSIX);
+ if (oldnametype == FILE_NAME_DOS) {
+ if (set_namespace(ni, dir_ni, longname, longlen,
+ FILE_NAME_WIN32_AND_DOS) >= 0) {
+ if (!ntfs_delete(vol,
+ (const char*)NULL, ni, dir_ni,
+ deletename, deletelen))
+ res = 0;
+ deleted = TRUE;
+ } else
+ done = TRUE;
+ }
+ }
+ if (!deleted) {
+ if (!done && (set_namespace(ni, dir_ni,
+ longname, longlen,
+ FILE_NAME_WIN32_AND_DOS) >= 0))
+ res = 0;
+ ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME);
+ ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME);
+ if (ntfs_inode_close_in_dir(ni,dir_ni) && !res)
+ res = -1;
+ if (ntfs_inode_close(dir_ni) && !res)
+ res = -1;
+ }
+ } else {
+ if (!ntfs_link_i(ni, dir_ni, shortname, shortlen,
+ FILE_NAME_DOS)
+ /* make sure a new link was recorded */
+ && (le16_to_cpu(ni->mrec->link_count) > linkcount)) {
+ /* delete the existing long name or short name */
+// is it ok to not provide the path ?
+ if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni,
+ deletename, deletelen)) {
+ /* delete closes the inodes, so have to open again */
+ dir_ni = ntfs_inode_open(vol, dnum);
+ if (dir_ni) {
+ ni = ntfs_inode_open(vol, fnum);
+ if (ni) {
+ if (!ntfs_link_i(ni, dir_ni,
+ longname, longlen,
+ FILE_NAME_WIN32))
+ res = 0;
+ if (ntfs_inode_close_in_dir(ni,
+ dir_ni)
+ && !res)
+ res = -1;
+ }
+ if (ntfs_inode_close(dir_ni) && !res)
+ res = -1;
+ }
+ }
+ } else {
+ ntfs_inode_close_in_dir(ni,dir_ni);
+ ntfs_inode_close(dir_ni);
+ }
+ }
+ return (res);
+}
+
+
+/*
+ * Set the ntfs DOS name into an extended attribute
+ *
+ * The DOS name will be added as another file name attribute
+ * using the existing file name information from the original
+ * name or overwriting the DOS Name if one exists.
+ *
+ * The inode of the file is always closed
+ */
+
+int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
+ const char *value, size_t size, int flags)
+{
+ int res = 0;
+ int longlen = 0;
+ int shortlen = 0;
+ char newname[MAX_DOS_NAME_LENGTH + 1];
+ ntfschar oldname[MAX_DOS_NAME_LENGTH];
+ int oldlen;
+ ntfs_volume *vol;
+ u64 fnum;
+ u64 dnum;
+ BOOL closed = FALSE;
+ ntfschar *shortname = NULL;
+ ntfschar longname[NTFS_MAX_NAME_LEN];
+
+ vol = ni->vol;
+ fnum = ni->mft_no;
+ /* convert the string to the NTFS wide chars */
+ if (size > MAX_DOS_NAME_LENGTH)
+ size = MAX_DOS_NAME_LENGTH;
+ strncpy(newname, value, size);
+ newname[size] = 0;
+ shortlen = ntfs_mbstoucs(newname, &shortname);
+ /* make sure the short name has valid chars */
+ if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) {
+ ntfs_inode_close_in_dir(ni,dir_ni);
+ ntfs_inode_close(dir_ni);
+ res = -errno;
+ return res;
+ }
+ dnum = dir_ni->mft_no;
+ longlen = get_long_name(ni, dnum, longname);
+ if (longlen > 0) {
+ oldlen = get_dos_name(ni, dnum, oldname);
+ if ((oldlen >= 0)
+ && !ntfs_forbidden_chars(longname, longlen)) {
+ if (oldlen > 0) {
+ if (flags & XATTR_CREATE) {
+ res = -1;
+ errno = EEXIST;
+ } else
+ if ((shortlen == oldlen)
+ && !memcmp(shortname,oldname,
+ oldlen*sizeof(ntfschar)))
+ /* already set, done */
+ res = 0;
+ else {
+ res = set_dos_name(ni, dir_ni,
+ shortname, shortlen,
+ longname, longlen,
+ oldname, oldlen, TRUE);
+ closed = TRUE;
+ }
+ } else {
+ if (flags & XATTR_REPLACE) {
+ res = -1;
+ errno = ENODATA;
+ } else {
+ res = set_dos_name(ni, dir_ni,
+ shortname, shortlen,
+ longname, longlen,
+ longname, longlen, FALSE);
+ closed = TRUE;
+ }
+ }
+ } else
+ res = -1;
+ } else {
+ res = -1;
+ errno = ENOENT;
+ }
+ free(shortname);
+ if (!closed) {
+ ntfs_inode_close_in_dir(ni,dir_ni);
+ ntfs_inode_close(dir_ni);
+ }
+ return (res ? -1 : 0);
+}
+
+/*
+ * Delete the ntfs DOS name
+ */
+
+int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni)
+{
+ int res;
+ int oldnametype;
+ int longlen = 0;
+ int shortlen;
+ u64 dnum;
+ ntfs_volume *vol;
+ BOOL deleted = FALSE;
+ ntfschar shortname[MAX_DOS_NAME_LENGTH];
+ ntfschar longname[NTFS_MAX_NAME_LEN];
+
+ res = -1;
+ vol = ni->vol;
+ dnum = dir_ni->mft_no;
+ longlen = get_long_name(ni, dnum, longname);
+ if (longlen > 0) {
+ shortlen = get_dos_name(ni, dnum, shortname);
+ if (shortlen >= 0) {
+ /* migrate the long name as Posix */
+ oldnametype = set_namespace(ni,dir_ni,longname,longlen,
+ FILE_NAME_POSIX);
+ switch (oldnametype) {
+ case FILE_NAME_WIN32_AND_DOS :
+ /* name was Win32+DOS : done */
+ res = 0;
+ break;
+ case FILE_NAME_DOS :
+ /* name was DOS, make it back to DOS */
+ set_namespace(ni,dir_ni,longname,longlen,
+ FILE_NAME_DOS);
+ errno = ENOENT;
+ break;
+ case FILE_NAME_WIN32 :
+ /* name was Win32, make it Posix and delete */
+ if (set_namespace(ni,dir_ni,shortname,shortlen,
+ FILE_NAME_POSIX) >= 0) {
+ if (!ntfs_delete(vol,
+ (const char*)NULL, ni,
+ dir_ni, shortname,
+ shortlen))
+ res = 0;
+ deleted = TRUE;
+ } else {
+ /*
+ * DOS name has been found, but cannot
+ * migrate to Posix : something bad
+ * has happened
+ */
+ errno = EIO;
+ ntfs_log_error("Could not change"
+ " DOS name of inode %lld to Posix\n",
+ (long long)ni->mft_no);
+ }
+ break;
+ default :
+ /* name was Posix or not found : error */
+ errno = ENOENT;
+ break;
+ }
+ }
+ } else {
+ errno = ENOENT;
+ res = -1;
+ }
+ if (!deleted) {
+ ntfs_inode_close_in_dir(ni,dir_ni);
+ ntfs_inode_close(dir_ni);
+ }
+ return (res);
+}
+
+#endif