/**
 * attrib.c - Attribute handling code. Originated from the Linux-NTFS project.
 *
 * Copyright (c) 2000-2010 Anton Altaparmakov
 * Copyright (c) 2002-2005 Richard Russon
 * Copyright (c) 2002-2008 Szabolcs Szakacsits
 * Copyright (c) 2004-2007 Yura Pakhuchiy
 * Copyright (c) 2007-2011 Jean-Pierre Andre
 * Copyright (c) 2010      Erik Larsson
 *
 * 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_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#ifdef __CELLOS_LV2__
#include "defines/cellos_lv2.h"
#endif

#include "param.h"
#include "compat.h"
#include "attrib.h"
#include "attrlist.h"
#include "device.h"
#include "mft.h"
#include "debug.h"
#include "mst.h"
#include "volume.h"
#include "types.h"
#include "layout.h"
#include "inode.h"
#include "runlist.h"
#include "lcnalloc.h"
#include "dir.h"
#include "compress.h"
#include "bitmap.h"
#include "logging.h"
#include "misc.h"
#include "efs.h"

ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') };
ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'),
			const_cpu_to_le16('S'),
			const_cpu_to_le16('D'),
			const_cpu_to_le16('S'),
			const_cpu_to_le16('\0') };

ntfschar TXF_DATA[] = { const_cpu_to_le16('$'),
			const_cpu_to_le16('T'),
			const_cpu_to_le16('X'),
			const_cpu_to_le16('F'),
			const_cpu_to_le16('_'),
			const_cpu_to_le16('D'),
			const_cpu_to_le16('A'),
			const_cpu_to_le16('T'),
			const_cpu_to_le16('A'),
			const_cpu_to_le16('\0') };

static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag)
{
	if (na->type == AT_DATA && na->name == AT_UNNAMED)
		return (na->ni->flags & flag);
	return 0;
}

static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag)
{
	if (na->type == AT_DATA && na->name == AT_UNNAMED)
		na->ni->flags |= flag;
	else
		ntfs_log_trace("Denied setting flag %d for not unnamed data "
			       "attribute\n", flag);
}

static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag)
{
	if (na->type == AT_DATA && na->name == AT_UNNAMED)
		na->ni->flags &= ~flag;
}

#define GenNAttrIno(func_name, flag)					\
int NAttr##func_name(ntfs_attr *na) { return NAttrFlag   (na, flag); } 	\
void NAttrSet##func_name(ntfs_attr *na)	 { NAttrSetFlag  (na, flag); }	\
void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); }

GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED)
GenNAttrIno(Encrypted, 	FILE_ATTR_ENCRYPTED)
GenNAttrIno(Sparse, 	FILE_ATTR_SPARSE_FILE)

/**
 * ntfs_get_attribute_value_length - Find the length of an attribute
 * @a:
 *
 * Description...
 *
 * Returns:
 */
s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a)
{
	if (!a) {
		errno = EINVAL;
		return 0;
	}
	errno = 0;
	if (a->non_resident)
		return sle64_to_cpu(a->data_size);
	
	return (s64)le32_to_cpu(a->value_length);
}

/**
 * ntfs_get_attribute_value - Get a copy of an attribute
 * @vol:	
 * @a:	
 * @b:	
 *
 * Description...
 *
 * Returns:
 */
s64 ntfs_get_attribute_value(const ntfs_volume *vol,
		const ATTR_RECORD *a, u8 *b)
{
	runlist *rl;
	s64 total, r;
	int i;

	/* Sanity checks. */
	if (!vol || !a || !b) {
		errno = EINVAL;
		return 0;
	}
	/* Complex attribute? */
	/*
	 * Ignore the flags in case they are not zero for an attribute list
	 * attribute.  Windows does not complain about invalid flags and chkdsk
	 * does not detect or fix them so we need to cope with it, too.
	 */
	if (a->type != AT_ATTRIBUTE_LIST && a->flags) {
		ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle "
			       "this yet.\n", le16_to_cpu(a->flags));
		errno = EOPNOTSUPP;
		return 0;
	}
	if (!a->non_resident) {
		/* Attribute is resident. */

		/* Sanity check. */
		if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset)
				> le32_to_cpu(a->length)) {
			return 0;
		}

		memcpy(b, (const char*)a + le16_to_cpu(a->value_offset),
				le32_to_cpu(a->value_length));
		errno = 0;
		return (s64)le32_to_cpu(a->value_length);
	}

	/* Attribute is not resident. */

	/* If no data, return 0. */
	if (!(a->data_size)) {
		errno = 0;
		return 0;
	}
	/*
	 * FIXME: What about attribute lists?!? (AIA)
	 */
	/* Decompress the mapping pairs array into a runlist. */
	rl = ntfs_mapping_pairs_decompress(vol, a, NULL);
	if (!rl) {
		errno = EINVAL;
		return 0;
	}
	/*
	 * FIXED: We were overflowing here in a nasty fashion when we
	 * reach the last cluster in the runlist as the buffer will
	 * only be big enough to hold data_size bytes while we are
	 * reading in allocated_size bytes which is usually larger
	 * than data_size, since the actual data is unlikely to have a
	 * size equal to a multiple of the cluster size!
	 * FIXED2:  We were also overflowing here in the same fashion
	 * when the data_size was more than one run smaller than the
	 * allocated size which happens with Windows XP sometimes.
	 */
	/* Now load all clusters in the runlist into b. */
	for (i = 0, total = 0; rl[i].length; i++) {
		if (total + (rl[i].length << vol->cluster_size_bits) >=
				sle64_to_cpu(a->data_size)) {
			unsigned char *intbuf = NULL;
			/*
			 * We have reached the last run so we were going to
			 * overflow when executing the ntfs_pread() which is
			 * BAAAAAAAD!
			 * Temporary fix:
			 *	Allocate a new buffer with size:
			 *	rl[i].length << vol->cluster_size_bits, do the
			 *	read into our buffer, then memcpy the correct
			 *	amount of data into the caller supplied buffer,
			 *	free our buffer, and continue.
			 * We have reached the end of data size so we were
			 * going to overflow in the same fashion.
			 * Temporary fix:  same as above.
			 */
			intbuf = ntfs_malloc(rl[i].length << vol->cluster_size_bits);
			if (!intbuf) {
				free(rl);
				return 0;
			}
			/*
			 * FIXME: If compressed file: Only read if lcn != -1.
			 * Otherwise, we are dealing with a sparse run and we
			 * just memset the user buffer to 0 for the length of
			 * the run, which should be 16 (= compression unit
			 * size).
			 * FIXME: Really only when file is compressed, or can
			 * we have sparse runs in uncompressed files as well?
			 * - Yes we can, in sparse files! But not necessarily
			 * size of 16, just run length.
			 */
			r = ntfs_pread(vol->dev, rl[i].lcn <<
					vol->cluster_size_bits, rl[i].length <<
					vol->cluster_size_bits, intbuf);
			if (r != rl[i].length << vol->cluster_size_bits) {
#define ESTR "Error reading attribute value"
				if (r == -1)
					ntfs_log_perror(ESTR);
				else if (r < rl[i].length <<
						vol->cluster_size_bits) {
					ntfs_log_debug(ESTR ": Ran out of input data.\n");
					errno = EIO;
				} else {
					ntfs_log_debug(ESTR ": unknown error\n");
					errno = EIO;
				}
#undef ESTR
				free(rl);
				free(intbuf);
				return 0;
			}
			memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) -
					total);
			free(intbuf);
			total = sle64_to_cpu(a->data_size);
			break;
		}
		/*
		 * FIXME: If compressed file: Only read if lcn != -1.
		 * Otherwise, we are dealing with a sparse run and we just
		 * memset the user buffer to 0 for the length of the run, which
		 * should be 16 (= compression unit size).
		 * FIXME: Really only when file is compressed, or can
		 * we have sparse runs in uncompressed files as well?
		 * - Yes we can, in sparse files! But not necessarily size of
		 * 16, just run length.
		 */
		r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits,
				rl[i].length << vol->cluster_size_bits,
				b + total);
		if (r != rl[i].length << vol->cluster_size_bits) {
#define ESTR "Error reading attribute value"
			if (r == -1)
				ntfs_log_perror(ESTR);
			else if (r < rl[i].length << vol->cluster_size_bits) {
				ntfs_log_debug(ESTR ": Ran out of input data.\n");
				errno = EIO;
			} else {
				ntfs_log_debug(ESTR ": unknown error\n");
				errno = EIO;
			}
#undef ESTR
			free(rl);
			return 0;
		}
		total += r;
	}
	free(rl);
	return total;
}

/* Already cleaned up code below, but still look for FIXME:... */

/**
 * __ntfs_attr_init - primary initialization of an ntfs attribute structure
 * @na:		ntfs attribute to initialize
 * @ni:		ntfs inode with which to initialize the ntfs attribute
 * @type:	attribute type
 * @name:	attribute name in little endian Unicode or NULL
 * @name_len:	length of attribute @name in Unicode characters (if @name given)
 *
 * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len.
 */
static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
		const ATTR_TYPES type, ntfschar *name, const u32 name_len)
{
	na->rl = NULL;
	na->ni = ni;
	na->type = type;
	na->name = name;
	if (name)
		na->name_len = name_len;
	else
		na->name_len = 0;
}

/**
 * ntfs_attr_init - initialize an ntfs_attr with data sizes and status
 * @na:
 * @non_resident:
 * @compressed:
 * @encrypted:
 * @sparse:
 * @allocated_size:
 * @data_size:
 * @initialized_size:
 * @compressed_size:
 * @compression_unit:
 *
 * Final initialization for an ntfs attribute.
 */
void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
		const ATTR_FLAGS data_flags,
		const BOOL encrypted, const BOOL sparse,
		const s64 allocated_size, const s64 data_size,
		const s64 initialized_size, const s64 compressed_size,
		const u8 compression_unit)
{
	if (!NAttrInitialized(na)) {
		na->data_flags = data_flags;
		if (non_resident)
			NAttrSetNonResident(na);
		if (data_flags & ATTR_COMPRESSION_MASK)
			NAttrSetCompressed(na);
		if (encrypted)
			NAttrSetEncrypted(na);
		if (sparse)
			NAttrSetSparse(na);
		na->allocated_size = allocated_size;
		na->data_size = data_size;
		na->initialized_size = initialized_size;
		if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) {
			ntfs_volume *vol = na->ni->vol;

			na->compressed_size = compressed_size;
			na->compression_block_clusters = 1 << compression_unit;
			na->compression_block_size = 1 << (compression_unit +
					vol->cluster_size_bits);
			na->compression_block_size_bits = ffs(
					na->compression_block_size) - 1;
		}
		NAttrSetInitialized(na);
	}
}

/**
 * ntfs_attr_open - open an ntfs attribute for access
 * @ni:		open ntfs inode in which the ntfs attribute resides
 * @type:	attribute type
 * @name:	attribute name in little endian Unicode or AT_UNNAMED or NULL
 * @name_len:	length of attribute @name in Unicode characters (if @name given)
 *
 * Allocate a new ntfs attribute structure, initialize it with @ni, @type,
 * @name, and @name_len, then return it. Return NULL on error with
 * errno set to the error code.
 *
 * If @name is AT_UNNAMED look specifically for an unnamed attribute.  If you
 * do not care whether the attribute is named or not set @name to NULL.  In
 * both those cases @name_len is not used at all.
 */
ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
		ntfschar *name, u32 name_len)
{
	ntfs_attr_search_ctx *ctx;
	ntfs_attr *na = NULL;
	ntfschar *newname = NULL;
	ATTR_RECORD *a;
	le16 cs;

	ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n",
		       (unsigned long long)ni->mft_no, type);
	
	if (!ni || !ni->vol || !ni->mrec) {
		errno = EINVAL;
		goto out;
	}
	na = ntfs_calloc(sizeof(ntfs_attr));
	if (!na)
		goto out;
	if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) {
		name = ntfs_ucsndup(name, name_len);
		if (!name)
			goto err_out;
		newname = name;
	}

	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx)
		goto err_out;

	if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx))
		goto put_err_out;

	a = ctx->attr;
	
	if (!name) {
		if (a->name_length) {
			name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu(
					a->name_offset)), a->name_length);
			if (!name)
				goto put_err_out;
			newname = name;
			name_len = a->name_length;
		} else {
			name = AT_UNNAMED;
			name_len = 0;
		}
	}
	
	__ntfs_attr_init(na, ni, type, name, name_len);
	
	/*
	 * Wipe the flags in case they are not zero for an attribute list
	 * attribute.  Windows does not complain about invalid flags and chkdsk
	 * does not detect or fix them so we need to cope with it, too.
	 */
	if (type == AT_ATTRIBUTE_LIST)
		a->flags = 0;

	if ((type == AT_DATA) && !a->initialized_size) {
		/*
		 * Define/redefine the compression state if stream is
		 * empty, based on the compression mark on parent
		 * directory (for unnamed data streams) or on current
		 * inode (for named data streams). The compression mark
		 * may change any time, the compression state can only
		 * change when stream is wiped out.
		 * 
		 * Also prevent compression on NTFS version < 3.0
		 * or cluster size > 4K or compression is disabled
		 */
		a->flags &= ~ATTR_COMPRESSION_MASK;
		if ((ni->flags & FILE_ATTR_COMPRESSED)
		    && (ni->vol->major_ver >= 3)
		    && NVolCompression(ni->vol)
		    && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE))
			a->flags |= ATTR_IS_COMPRESSED;
	}
	
	cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);

	/* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */
	if (na->type == AT_DATA && na->name == AT_UNNAMED &&
	    (((a->flags & ATTR_IS_SPARSE)     && !NAttrSparse(na)) ||
	     (!(a->flags & ATTR_IS_ENCRYPTED)  != !NAttrEncrypted(na)))) {
		errno = EIO;
		ntfs_log_perror("Inode %lld has corrupt attribute flags "
				"(0x%x <> 0x%x)",(unsigned long long)ni->mft_no,
				a->flags, na->ni->flags);
		goto put_err_out;
	}

	if (a->non_resident) {
		if ((a->flags & ATTR_COMPRESSION_MASK)
				 && !a->compression_unit) {
			errno = EIO;
			ntfs_log_perror("Compressed inode %lld attr 0x%x has "
					"no compression unit",
					(unsigned long long)ni->mft_no, type);
			goto put_err_out;
		}
		ntfs_attr_init(na, TRUE, a->flags,
				a->flags & ATTR_IS_ENCRYPTED,
				a->flags & ATTR_IS_SPARSE,
				sle64_to_cpu(a->allocated_size),
				sle64_to_cpu(a->data_size),
				sle64_to_cpu(a->initialized_size),
				cs ? sle64_to_cpu(a->compressed_size) : 0,
				cs ? a->compression_unit : 0);
	} else {
		s64 l = le32_to_cpu(a->value_length);
		ntfs_attr_init(na, FALSE, a->flags,
				a->flags & ATTR_IS_ENCRYPTED,
				a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l,
				cs ? (l + 7) & ~7 : 0, 0);
	}
	ntfs_attr_put_search_ctx(ctx);
out:
	ntfs_log_leave("\n");	
	return na;

put_err_out:
	ntfs_attr_put_search_ctx(ctx);
err_out:
	free(newname);
	free(na);
	na = NULL;
	goto out;
}

/**
 * ntfs_attr_close - free an ntfs attribute structure
 * @na:		ntfs attribute structure to free
 *
 * Release all memory associated with the ntfs attribute @na and then release
 * @na itself.
 */
void ntfs_attr_close(ntfs_attr *na)
{
	if (!na)
		return;
	if (NAttrNonResident(na) && na->rl)
		free(na->rl);
	/* Don't release if using an internal constant. */
	if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30
				&& na->name != STREAM_SDS)
		free(na->name);
	free(na);
}

/**
 * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute
 * @na:		ntfs attribute for which to map (part of) a runlist
 * @vcn:	map runlist part containing this vcn
 *
 * Map the part of a runlist containing the @vcn of the ntfs attribute @na.
 *
 * Return 0 on success and -1 on error with errno set to the error code.
 */
int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn)
{
	LCN lcn;
	ntfs_attr_search_ctx *ctx;

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n",
		(unsigned long long)na->ni->mft_no, na->type, (long long)vcn);

	lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
	if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT)
		return 0;

	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx)
		return -1;

	/* Find the attribute in the mft record. */
	if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
			vcn, NULL, 0, ctx)) {
		runlist_element *rl;

		/* Decode the runlist. */
		rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr,
				na->rl);
		if (rl) {
			na->rl = rl;
			ntfs_attr_put_search_ctx(ctx);
			return 0;
		}
	}
	
	ntfs_attr_put_search_ctx(ctx);
	return -1;
}

#if PARTIAL_RUNLIST_UPDATING

/*
 *		Map the runlist of an attribute from some point to the end
 *
 *	Returns 0 if success,
 *		-1 if it failed (errno telling why)
 */

static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn)
{
	LCN lcn;
	VCN last_vcn;
	VCN highest_vcn;
	VCN needed;
	VCN existing_vcn;
	runlist_element *rl;
	ATTR_RECORD *a;
	BOOL startseen;
	ntfs_attr_search_ctx *ctx;

	lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
	if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT)
		return 0;

	existing_vcn = (na->rl ? na->rl->vcn : -1);

	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx)
		return -1;

	/* Get the last vcn in the attribute. */
	last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits;

	needed = vcn;
	highest_vcn = 0;
	startseen = FALSE;
	do {
		/* Find the attribute in the mft record. */
		if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
				needed, NULL, 0, ctx)) {

			a = ctx->attr;
			/* Decode and merge the runlist. */
			rl = ntfs_mapping_pairs_decompress(na->ni->vol, a,
					na->rl);
			if (rl) {
				na->rl = rl;
				highest_vcn = le64_to_cpu(a->highest_vcn);
				/* corruption detection */
				if (((highest_vcn + 1) < last_vcn)
				    && ((highest_vcn + 1) <= needed)) {
					ntfs_log_error("Corrupt attribute list\n");
					rl = (runlist_element*)NULL;
				}
				needed = highest_vcn + 1;
				if (!a->lowest_vcn)
					startseen = TRUE;
				/* reaching a previously allocated part ? */
				if ((existing_vcn >= 0)
				    && (needed >= existing_vcn)) {
					needed = last_vcn;
				}
			}
		} else
			rl = (runlist_element*)NULL;
	} while (rl && (needed < last_vcn));
	ntfs_attr_put_search_ctx(ctx);
		/* mark fully mapped if we did so */
	if (rl && startseen)
		NAttrSetFullyMapped(na);
	return (rl ? 0 : -1);
}

#endif

/**
 * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute
 * @na:		ntfs attribute for which to map the runlist
 *
 * Map the whole runlist of the ntfs attribute @na.  For an attribute made up
 * of only one attribute extent this is the same as calling
 * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this
 * will map the runlist fragments from each of the extents thus giving access
 * to the entirety of the disk allocation of an attribute.
 *
 * Return 0 on success and -1 on error with errno set to the error code.
 */
int ntfs_attr_map_whole_runlist(ntfs_attr *na)
{
	VCN next_vcn, last_vcn, highest_vcn;
	ntfs_attr_search_ctx *ctx;
	ntfs_volume *vol = na->ni->vol;
	ATTR_RECORD *a;
	int ret = -1;

	ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n",
		       (unsigned long long)na->ni->mft_no, na->type);

		/* avoid multiple full runlist mappings */
	if (NAttrFullyMapped(na)) {
		ret = 0;
		goto out;
	}
	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx)
		goto out;

	/* Map all attribute extents one by one. */
	next_vcn = last_vcn = highest_vcn = 0;
	a = NULL;
	while (1) {
		runlist_element *rl;

		int not_mapped = 0;
		if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED)
			not_mapped = 1;

		if (ntfs_attr_lookup(na->type, na->name, na->name_len,
				CASE_SENSITIVE, next_vcn, NULL, 0, ctx))
			break;

		a = ctx->attr;

		if (not_mapped) {
			/* Decode the runlist. */
			rl = ntfs_mapping_pairs_decompress(na->ni->vol,
								a, na->rl);
			if (!rl)
				goto err_out;
			na->rl = rl;
		}

		/* Are we in the first extent? */
		if (!next_vcn) {
			 if (a->lowest_vcn) {
				 errno = EIO;
				 ntfs_log_perror("First extent of inode %llu "
					"attribute has non-zero lowest_vcn",
					(unsigned long long)na->ni->mft_no);
				 goto err_out;
			}
			/* Get the last vcn in the attribute. */
			last_vcn = sle64_to_cpu(a->allocated_size) >>
					vol->cluster_size_bits;
		}

		/* Get the lowest vcn for the next extent. */
		highest_vcn = sle64_to_cpu(a->highest_vcn);
		next_vcn = highest_vcn + 1;

		/* Only one extent or error, which we catch below. */
		if (next_vcn <= 0) {
			errno = ENOENT;
			break;
		}

		/* Avoid endless loops due to corruption. */
		if (next_vcn < sle64_to_cpu(a->lowest_vcn)) {
			errno = EIO;
			ntfs_log_perror("Inode %llu has corrupt attribute list",
					(unsigned long long)na->ni->mft_no);
			goto err_out;
		}
	}
	if (!a) {
		ntfs_log_perror("Couldn't find attribute for runlist mapping");
		goto err_out;
	}
	if (highest_vcn && highest_vcn != last_vcn - 1) {
		errno = EIO;
		ntfs_log_perror("Failed to load full runlist: inode: %llu "
				"highest_vcn: 0x%llx last_vcn: 0x%llx",
				(unsigned long long)na->ni->mft_no, 
				(long long)highest_vcn, (long long)last_vcn);
		goto err_out;
	}
	if (errno == ENOENT) {
		NAttrSetFullyMapped(na);
		ret = 0;
	}
err_out:	
	ntfs_attr_put_search_ctx(ctx);
out:
	ntfs_log_leave("\n");
	return ret;
}

/**
 * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute
 * @na:		ntfs attribute whose runlist to use for conversion
 * @vcn:	vcn to convert
 *
 * Convert the virtual cluster number @vcn of an attribute into a logical
 * cluster number (lcn) of a device using the runlist @na->rl to map vcns to
 * their corresponding lcns.
 *
 * If the @vcn is not mapped yet, attempt to map the attribute extent
 * containing the @vcn and retry the vcn to lcn conversion.
 *
 * Since lcns must be >= 0, we use negative return values with special meaning:
 *
 * Return value		Meaning / Description
 * ==========================================
 *  -1 = LCN_HOLE	Hole / not allocated on disk.
 *  -3 = LCN_ENOENT	There is no such vcn in the attribute.
 *  -4 = LCN_EINVAL	Input parameter error.
 *  -5 = LCN_EIO	Corrupt fs, disk i/o error, or not enough memory.
 */
LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn)
{
	LCN lcn;
	BOOL is_retry = FALSE;

	if (!na || !NAttrNonResident(na) || vcn < 0)
		return (LCN)LCN_EINVAL;

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
			long)na->ni->mft_no, na->type);
retry:
	/* Convert vcn to lcn. If that fails map the runlist and retry once. */
	lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
	if (lcn >= 0)
		return lcn;
	if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) {
		is_retry = TRUE;
		goto retry;
	}
	/*
	 * If the attempt to map the runlist failed, or we are getting
	 * LCN_RL_NOT_MAPPED despite having mapped the attribute extent
	 * successfully, something is really badly wrong...
	 */
	if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED)
		return (LCN)LCN_EIO;
	/* lcn contains the appropriate error code. */
	return lcn;
}

/**
 * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute
 * @na:		ntfs attribute whose runlist to search
 * @vcn:	vcn to find
 *
 * Find the virtual cluster number @vcn in the runlist of the ntfs attribute
 * @na and return the the address of the runlist element containing the @vcn.
 *
 * Note you need to distinguish between the lcn of the returned runlist
 * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes
 * on read and allocate clusters on write. You need to update the runlist, the
 * attribute itself as well as write the modified mft record to disk.
 *
 * If there is an error return NULL with errno set to the error code. The
 * following error codes are defined:
 *	EINVAL		Input parameter error.
 *	ENOENT		There is no such vcn in the runlist.
 *	ENOMEM		Not enough memory.
 *	EIO		I/O error or corrupt metadata.
 */
runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn)
{
	runlist_element *rl;
	BOOL is_retry = FALSE;

	if (!na || !NAttrNonResident(na) || vcn < 0) {
		errno = EINVAL;
		return NULL;
	}

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n",
		       (unsigned long long)na->ni->mft_no, na->type,
		       (long long)vcn);
retry:
	rl = na->rl;
	if (!rl)
		goto map_rl;
	if (vcn < rl[0].vcn)
		goto map_rl;
	while (rl->length) {
		if (vcn < rl[1].vcn) {
			if (rl->lcn >= (LCN)LCN_HOLE)
				return rl;
			break;
		}
		rl++;
	}
	switch (rl->lcn) {
	case (LCN)LCN_RL_NOT_MAPPED:
		goto map_rl;
	case (LCN)LCN_ENOENT:
		errno = ENOENT;
		break;
	case (LCN)LCN_EINVAL:
		errno = EINVAL;
		break;
	default:
		errno = EIO;
		break;
	}
	return NULL;
map_rl:
	/* The @vcn is in an unmapped region, map the runlist and retry. */
	if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) {
		is_retry = TRUE;
		goto retry;
	}
	/*
	 * If we already retried or the mapping attempt failed something has
	 * gone badly wrong. EINVAL and ENOENT coming from a failed mapping
	 * attempt are equivalent to errors for us as they should not happen
	 * in our code paths.
	 */
	if (is_retry || errno == EINVAL || errno == ENOENT)
		errno = EIO;
	return NULL;
}

/**
 * ntfs_attr_pread_i - see description at ntfs_attr_pread()
 */ 
static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b)
{
	s64 br, to_read, ofs, total, total2, max_read, max_init;
	ntfs_volume *vol;
	runlist_element *rl;
	u16 efs_padding_length;

	/* Sanity checking arguments is done in ntfs_attr_pread(). */
	
	if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) {
		if ((na->data_flags & ATTR_COMPRESSION_MASK)
		    == ATTR_IS_COMPRESSED)
			return ntfs_compressed_attr_pread(na, pos, count, b);
		else {
				/* compression mode not supported */
			errno = EOPNOTSUPP;
			return -1;
		}
	}
	/*
	 * Encrypted non-resident attributes are not supported.  We return
	 * access denied, which is what Windows NT4 does, too.
	 * However, allow if mounted with efs_raw option
	 */
	vol = na->ni->vol;
	if (!vol->efs_raw && NAttrEncrypted(na) && NAttrNonResident(na)) {
		errno = EACCES;
		return -1;
	}
	
	if (!count)
		return 0;
		/*
		 * Truncate reads beyond end of attribute,
		 * but round to next 512 byte boundary for encrypted
		 * attributes with efs_raw mount option
		 */
	max_read = na->data_size;
	max_init = na->initialized_size;
	if (na->ni->vol->efs_raw
	    && (na->data_flags & ATTR_IS_ENCRYPTED)
	    && NAttrNonResident(na)) {
		if (na->data_size != na->initialized_size) {
			ntfs_log_error("uninitialized encrypted file not supported\n");
			errno = EINVAL;
			return -1;
		}	
		max_init = max_read = ((na->data_size + 511) & ~511) + 2;
	}
	if (pos + count > max_read) {
		if (pos >= max_read)
			return 0;
		count = max_read - pos;
	}
	/* If it is a resident attribute, get the value from the mft record. */
	if (!NAttrNonResident(na)) {
		ntfs_attr_search_ctx *ctx;
		char *val;

		ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
		if (!ctx)
			return -1;
		if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
				0, NULL, 0, ctx)) {
res_err_out:
			ntfs_attr_put_search_ctx(ctx);
			return -1;
		}
		val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset);
		if (val < (char*)ctx->attr || val +
				le32_to_cpu(ctx->attr->value_length) >
				(char*)ctx->mrec + vol->mft_record_size) {
			errno = EIO;
			ntfs_log_perror("%s: Sanity check failed", __FUNCTION__);
			goto res_err_out;
		}
		memcpy(b, val + pos, count);
		ntfs_attr_put_search_ctx(ctx);
		return count;
	}
	total = total2 = 0;
	/* Zero out reads beyond initialized size. */
	if (pos + count > max_init) {
		if (pos >= max_init) {
			memset(b, 0, count);
			return count;
		}
		total2 = pos + count - max_init;
		count -= total2;
		memset((u8*)b + count, 0, total2);
	}
		/*
		 * for encrypted non-resident attributes with efs_raw set 
		 * the last two bytes aren't read from disk but contain
		 * the number of padding bytes so original size can be 
		 * restored
		 */
	if (na->ni->vol->efs_raw && 
			(na->data_flags & ATTR_IS_ENCRYPTED) && 
			((pos + count) > max_init-2)) {
		efs_padding_length = 511 - ((na->data_size - 1) & 511);
		if (pos+count == max_init) {
			if (count == 1) {
				*((u8*)b+count-1) = (u8)(efs_padding_length >> 8);
				count--;
				total2++;
			} else {
				*(u16*)((u8*)b+count-2) = cpu_to_le16(efs_padding_length);
				count -= 2;
				total2 +=2;
			}
		} else {
			*((u8*)b+count-1) = (u8)(efs_padding_length & 0xff);
			count--;
			total2++;
		}
	}
	
	/* Find the runlist element containing the vcn. */
	rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
	if (!rl) {
		/*
		 * If the vcn is not present it is an out of bounds read.
		 * However, we already truncated the read to the data_size,
		 * so getting this here is an error.
		 */
		if (errno == ENOENT) {
			errno = EIO;
			ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__);
		}
		return -1;
	}
	/*
	 * Gather the requested data into the linear destination buffer. Note,
	 * a partial final vcn is taken care of by the @count capping of read
	 * length.
	 */
	ofs = pos - (rl->vcn << vol->cluster_size_bits);
	for (; count; rl++, ofs = 0) {
		if (rl->lcn == LCN_RL_NOT_MAPPED) {
			rl = ntfs_attr_find_vcn(na, rl->vcn);
			if (!rl) {
				if (errno == ENOENT) {
					errno = EIO;
					ntfs_log_perror("%s: Failed to find VCN #2",
							__FUNCTION__);
				}
				goto rl_err_out;
			}
			/* Needed for case when runs merged. */
			ofs = pos + total - (rl->vcn << vol->cluster_size_bits);
		}
		if (!rl->length) {
			errno = EIO;
			ntfs_log_perror("%s: Zero run length", __FUNCTION__);
			goto rl_err_out;
		}
		if (rl->lcn < (LCN)0) {
			if (rl->lcn != (LCN)LCN_HOLE) {
				ntfs_log_perror("%s: Bad run (%lld)", 
						__FUNCTION__,
						(long long)rl->lcn);
				goto rl_err_out;
			}
			/* It is a hole, just zero the matching @b range. */
			to_read = min(count, (rl->length <<
					vol->cluster_size_bits) - ofs);
			memset(b, 0, to_read);
			/* Update progress counters. */
			total += to_read;
			count -= to_read;
			b = (u8*)b + to_read;
			continue;
		}
		/* It is a real lcn, read it into @dst. */
		to_read = min(count, (rl->length << vol->cluster_size_bits) -
				ofs);
retry:
		ntfs_log_trace("Reading %lld bytes from vcn %lld, lcn %lld, ofs"
				" %lld.\n", (long long)to_read, (long long)rl->vcn,
			       (long long )rl->lcn, (long long)ofs);
		br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) +
				ofs, to_read, b);
		/* If everything ok, update progress counters and continue. */
		if (br > 0) {
			total += br;
			count -= br;
			b = (u8*)b + br;
		}
		if (br == to_read)
			continue;
		/* If the syscall was interrupted, try again. */
		if (br == (s64)-1 && errno == EINTR)
			goto retry;
		if (total)
			return total;
		if (!br)
			errno = EIO;
		ntfs_log_perror("%s: ntfs_pread failed", __FUNCTION__);
		return -1;
	}
	/* Finally, return the number of bytes read. */
	return total + total2;
rl_err_out:
	if (total)
		return total;
	errno = EIO;
	return -1;
}

/**
 * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure
 * @na:		ntfs attribute to read from
 * @pos:	byte position in the attribute to begin reading from
 * @count:	number of bytes to read
 * @b:		output data buffer
 *
 * This function will read @count bytes starting at offset @pos from the ntfs
 * attribute @na into the data buffer @b.
 *
 * On success, return the number of successfully read bytes. If this number is
 * lower than @count this means that the read reached end of file or that an
 * error was encountered during the read so that the read is partial. 0 means
 * end of file or nothing was read (also return 0 when @count is 0).
 *
 * On error and nothing has been read, return -1 with errno set appropriately
 * to the return code of ntfs_pread(), or to EINVAL in case of invalid
 * arguments.
 */
s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b)
{
	s64 ret;
	
	if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
		errno = EINVAL;
		ntfs_log_perror("%s: na=%p  b=%p  pos=%lld  count=%lld",
				__FUNCTION__, na, b, (long long)pos,
				(long long)count);
		return -1;
	}
	
	ntfs_log_enter("Entering for inode %lld attr 0x%x pos %lld count "
		       "%lld\n", (unsigned long long)na->ni->mft_no,
		       na->type, (long long)pos, (long long)count);

	ret = ntfs_attr_pread_i(na, pos, count, b);
	
	ntfs_log_leave("\n");
	return ret;
}

static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count)
{
	char *buf;
	s64 written, size, end = pos + count;
	s64 ofsi;
	const runlist_element *rli;
	ntfs_volume *vol;
	int ret = -1;

	ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, 
		       (long long)count);
	
	if (!na || pos < 0 || count < 0) {
		errno = EINVAL;
		goto err_out;
	}
	
	buf = ntfs_calloc(NTFS_BUF_SIZE);
	if (!buf)
		goto err_out;
	
	rli = na->rl;
	ofsi = 0;
	vol = na->ni->vol;
	while (pos < end) {
		while (rli->length && (ofsi + (rli->length <<
	                        vol->cluster_size_bits) <= pos)) {
	                ofsi += (rli->length << vol->cluster_size_bits);
			rli++;
		}
		size = min(end - pos, NTFS_BUF_SIZE);
			/*
			 * If the zeroed block is fully within a hole,
			 * we need not write anything, so advance as far
			 * as possible within the hole.
			 */
		if ((rli->lcn == (LCN)LCN_HOLE)
		    && (ofsi <= pos)
		    && (ofsi + (rli->length << vol->cluster_size_bits)
				>= (pos + size))) {
			size = min(end - pos, ofsi - pos
				+ (rli->length << vol->cluster_size_bits));
			pos += size;
		} else {
			written = ntfs_rl_pwrite(vol, rli, ofsi, pos,
							size, buf);
			if (written <= 0) {
				ntfs_log_perror("Failed to zero space");
				goto err_free;
			}
			pos += written;
		}
	}
	
	ret = 0;
err_free:	
	free(buf);
err_out:
	return ret;	
}

static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, 
			       runlist_element **rl, VCN *update_from)
{
	s64 to_write;
	s64 need;
	ntfs_volume *vol = na->ni->vol;
	int eo, ret = -1;
	runlist *rlc;
	LCN lcn_seek_from = -1;
	VCN cur_vcn, from_vcn;

	to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs);
	
	cur_vcn = (*rl)->vcn;
	from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits);
	
	ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: "
		       "%lld\n", (long long)count, (long long)cur_vcn, 
		       (long long)from_vcn, (long long)to_write, (long long)*ofs);
	 
	/* Map the runlist to be able to update mapping pairs later. */
#if PARTIAL_RUNLIST_UPDATING
	if ((!na->rl
	    || !NAttrDataAppending(na))) {
		if (ntfs_attr_map_whole_runlist(na))
			goto err_out;
	} else {
		/* make sure the previous non-hole is mapped */
		rlc = *rl;
		rlc--;
		if (((*rl)->lcn == LCN_HOLE)
		    && cur_vcn
		    && (rlc->vcn < 0)) {
			if (ntfs_attr_map_partial_runlist(na, cur_vcn - 1))
				goto err_out;
		}
	}
#else
	if (ntfs_attr_map_whole_runlist(na))
		goto err_out;
#endif
	 
	/* Restore @*rl, it probably get lost during runlist mapping. */
	*rl = ntfs_attr_find_vcn(na, cur_vcn);
	if (!*rl) {
		ntfs_log_error("Failed to find run after mapping runlist. "
			       "Please report to %s.\n", NTFS_DEV_LIST);
		errno = EIO;
		goto err_out;
	}
	
	/* Search backwards to find the best lcn to start seek from. */
	rlc = *rl;
	while (rlc->vcn) {
		rlc--;
		if (rlc->lcn >= 0) {
				/*
				 * avoid fragmenting a compressed file
				 * Windows does not do that, and that may
				 * not be desirable for files which can
				 * be updated
				 */
			if (na->data_flags & ATTR_COMPRESSION_MASK)
				lcn_seek_from = rlc->lcn + rlc->length;
			else
				lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn);
			break;
		}
	}
	if (lcn_seek_from == -1) {
		/* Backwards search failed, search forwards. */
		rlc = *rl;
		while (rlc->length) {
			rlc++;
			if (rlc->lcn >= 0) {
				lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn);
				if (lcn_seek_from < -1)
					lcn_seek_from = -1;
				break;
			}
		}
	}
	
	need = ((*ofs + to_write - 1) >> vol->cluster_size_bits)
			 + 1 + (*rl)->vcn - from_vcn;
	if ((na->data_flags & ATTR_COMPRESSION_MASK)
	    && (need < na->compression_block_clusters)) {
		/*
		 * for a compressed file, be sure to allocate the full
		 * compression block, as we may need space to decompress
		 * existing compressed data.
		 * So allocate the space common to compression block
		 * and existing hole.
		 */
		VCN alloc_vcn;

		if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn)
			alloc_vcn = (*rl)->vcn;
		else
			alloc_vcn = from_vcn & -na->compression_block_clusters;
		need = (alloc_vcn | (na->compression_block_clusters - 1))
			+ 1 - alloc_vcn;
		if (need > (*rl)->length) {
			ntfs_log_error("Cannot allocate %lld clusters"
					" within a hole of %lld\n",
					(long long)need,
					(long long)(*rl)->length);
			errno = EIO;
			goto err_out;
		}
		rlc = ntfs_cluster_alloc(vol, alloc_vcn, need,
				 lcn_seek_from, DATA_ZONE);
	} else
		rlc = ntfs_cluster_alloc(vol, from_vcn, need,
				 lcn_seek_from, DATA_ZONE);
	if (!rlc)
		goto err_out;
	if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE))
		na->compressed_size += need << vol->cluster_size_bits;
	
	*rl = ntfs_runlists_merge(na->rl, rlc);
		/*
		 * For a compressed attribute, we must be sure there are two
		 * available entries, so reserve them before it gets too late.
		 */
	if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) {
		runlist_element *oldrl = na->rl;
		na->rl = *rl;
		*rl = ntfs_rl_extend(na,*rl,2);
		if (!*rl) na->rl = oldrl; /* restore to original if failed */
	}
	if (!*rl) {
		eo = errno;
		ntfs_log_perror("Failed to merge runlists");
		if (ntfs_cluster_free_from_rl(vol, rlc)) {
			ntfs_log_perror("Failed to free hot clusters. "
					"Please run chkdsk /f");
		}
		errno = eo;
		goto err_out;
	}
	na->unused_runs = 2;
	na->rl = *rl;
	if ((*update_from == -1) || (from_vcn < *update_from))
		*update_from = from_vcn;
	*rl = ntfs_attr_find_vcn(na, cur_vcn);
	if (!*rl) {
		/*
		 * It's definitely a BUG, if we failed to find @cur_vcn, because
		 * we missed it during instantiating of the hole.
		 */
		ntfs_log_error("Failed to find run after hole instantiation. "
			       "Please report to %s.\n", NTFS_DEV_LIST);
		errno = EIO;
		goto err_out;
	}
	/* If leaved part of the hole go to the next run. */
	if ((*rl)->lcn < 0)
		(*rl)++;
	/* Now LCN shoudn't be less than 0. */
	if ((*rl)->lcn < 0) {
		ntfs_log_error("BUG! LCN is lesser than 0. "
			       "Please report to the %s.\n", NTFS_DEV_LIST);
		errno = EIO;
		goto err_out;
	}
	if (*ofs) {
		/* Clear non-sparse region from @cur_vcn to @*ofs. */
		if (ntfs_attr_fill_zero(na, cur_vcn << vol->cluster_size_bits,
					*ofs))
			goto err_out;
	}
	if ((*rl)->vcn < cur_vcn) {
		/*
		 * Clusters that replaced hole are merged with
		 * previous run, so we need to update offset.
		 */
		*ofs += (cur_vcn - (*rl)->vcn) << vol->cluster_size_bits;
	}
	if ((*rl)->vcn > cur_vcn) {
		/*
		 * We left part of the hole, so we need to update offset
		 */
		*ofs -= ((*rl)->vcn - cur_vcn) << vol->cluster_size_bits;
	}
	
	ret = 0;
err_out:
	return ret;
}

static int stuff_hole(ntfs_attr *na, const s64 pos);

/*
 *		Split an existing hole for overwriting with data
 *	The hole may have to be split into two or three parts, so
 *	that the overwritten part fits within a single compression block
 *
 *	No cluster allocation is needed, this will be done later in
 *	standard hole filling, hence no need to reserve runs for
 *	future needs.
 *
 *	Returns the number of clusters with existing compressed data
 *		in the compression block to be written to
 *		(or the full block, if it was a full hole)
 *		-1 if there were an error
 */

static int split_compressed_hole(ntfs_attr *na, runlist_element **prl,
    		s64 pos, s64 count, VCN *update_from)
{
	int compressed_part;
	int cluster_size_bits = na->ni->vol->cluster_size_bits;
	runlist_element *rl = *prl;

	compressed_part
		= na->compression_block_clusters;
		/* reserve entries in runlist if we have to split */
	if (rl->length > na->compression_block_clusters) {
		*prl = ntfs_rl_extend(na,*prl,2);
		if (!*prl) {
			compressed_part = -1;
		} else {
			rl = *prl;
			na->unused_runs = 2;
		}
	}
	if (*prl && (rl->length > na->compression_block_clusters)) {
		/*
		 * Locate the update part relative to beginning of
		 * current run
		 */
		int beginwrite = (pos >> cluster_size_bits) - rl->vcn;
		s32 endblock = (((pos + count - 1) >> cluster_size_bits)
			| (na->compression_block_clusters - 1)) + 1 - rl->vcn;

		compressed_part = na->compression_block_clusters
			- (rl->length & (na->compression_block_clusters - 1));
		if ((beginwrite + compressed_part) >= na->compression_block_clusters)
			compressed_part = na->compression_block_clusters;
			/*
			 * if the run ends beyond end of needed block
			 * we have to split the run
			 */
		if (endblock < rl[0].length) {
			runlist_element *xrl;
			int n;

			/*
			 * we have to split into three parts if the run
			 * does not end within the first compression block.
			 * This means the hole begins before the
			 * compression block.
			 */
			if (endblock > na->compression_block_clusters) {
				if (na->unused_runs < 2) {
ntfs_log_error("No free run, case 1\n");
				}
				na->unused_runs -= 2;
				xrl = rl;
				n = 0;
				while (xrl->length) {
					xrl++;
					n++;
				}
				do {
					xrl[2] = *xrl;
					xrl--;
				} while (xrl != rl);
				rl[1].length = na->compression_block_clusters;
				rl[2].length = rl[0].length - endblock;
				rl[0].length = endblock
					- na->compression_block_clusters;
				rl[1].lcn = LCN_HOLE;
				rl[2].lcn = LCN_HOLE;
				rl[1].vcn = rl[0].vcn + rl[0].length;
				rl[2].vcn = rl[1].vcn
					+ na->compression_block_clusters;
				rl = ++(*prl);
			} else {
				/*
				 * split into two parts and use the
				 * first one
				 */
				if (!na->unused_runs) {
ntfs_log_error("No free run, case 2\n");
				}
				na->unused_runs--;
				xrl = rl;
				n = 0;
				while (xrl->length) {
					xrl++;
					n++;
				}
				do {
					xrl[1] = *xrl;
					xrl--;
				} while (xrl != rl);
				if (beginwrite < endblock) {
					/* we will write into the first part of hole */
					rl[1].length = rl[0].length - endblock;
					rl[0].length = endblock;
					rl[1].vcn = rl[0].vcn + rl[0].length;
					rl[1].lcn = LCN_HOLE;
				} else {
					/* we will write into the second part of hole */
// impossible ?
					rl[1].length = rl[0].length - endblock;
					rl[0].length = endblock;
					rl[1].vcn = rl[0].vcn + rl[0].length;
					rl[1].lcn = LCN_HOLE;
					rl = ++(*prl);
				}
			}
		} else {
			if (rl[1].length) {
				runlist_element *xrl;
				int n;

				/*
				 * split into two parts and use the
				 * last one
				 */
				if (!na->unused_runs) {
ntfs_log_error("No free run, case 4\n");
				}
				na->unused_runs--;
				xrl = rl;
				n = 0;
				while (xrl->length) {
					xrl++;
					n++;
				}
				do {
					xrl[1] = *xrl;
					xrl--;
				} while (xrl != rl);
			} else {
				rl[2].lcn = rl[1].lcn;
				rl[2].vcn = rl[1].vcn;
				rl[2].length = rl[1].length;
			}
			rl[1].vcn -= na->compression_block_clusters;
			rl[1].lcn = LCN_HOLE;
			rl[1].length = na->compression_block_clusters;
			rl[0].length -= na->compression_block_clusters;
			if (pos >= (rl[1].vcn << cluster_size_bits)) {
				rl = ++(*prl);
			}
		}
	if ((*update_from == -1) || ((*prl)->vcn < *update_from))
		*update_from = (*prl)->vcn;
	}
	return (compressed_part);
}

/*
 *		Borrow space from adjacent hole for appending data
 *	The hole may have to be split so that the end of hole is not
 *	affected by cluster allocation and overwriting
 *	Cluster allocation is needed for the overwritten compression block
 *
 *	Must always leave two unused entries in the runlist
 *
 *	Returns the number of clusters with existing compressed data
 *		in the compression block to be written to
 *		-1 if there were an error
 */

static int borrow_from_hole(ntfs_attr *na, runlist_element **prl,
    		s64 pos, s64 count, VCN *update_from, BOOL wasnonresident)
{
	int compressed_part = 0;
	int cluster_size_bits = na->ni->vol->cluster_size_bits;
	runlist_element *rl = *prl;
	s32 endblock;
	long long allocated;
	runlist_element *zrl;
	int irl;
	BOOL undecided;
	BOOL nothole;

		/* check whether the compression block is fully allocated */
	endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn;
	allocated = 0;
	zrl = rl;
	irl = 0;
	while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) {
		allocated += zrl->length;
		zrl++;
		irl++;
	}

	undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED);
	nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE);

	if (undecided || nothole) {
		runlist_element *orl = na->rl;
		s64 olcn = (*prl)->lcn;
#if PARTIAL_RUNLIST_UPDATING
		VCN prevblock;
#endif
			/*
			 * Map the runlist, unless it has not been created.
			 * If appending data, a partial mapping from the
			 * end of previous block will do.
			 */
		irl = *prl - na->rl;
#if PARTIAL_RUNLIST_UPDATING
		prevblock = pos >> cluster_size_bits;
		if (prevblock)
			prevblock--;
		if (!NAttrBeingNonResident(na)
		    && (NAttrDataAppending(na)
			? ntfs_attr_map_partial_runlist(na,prevblock)
			: ntfs_attr_map_whole_runlist(na))) {
#else
		if (!NAttrBeingNonResident(na)
			&& ntfs_attr_map_whole_runlist(na)) {
#endif
			rl = (runlist_element*)NULL;
		} else {
			/*
			 * Mapping the runlist may cause its relocation,
			 * and relocation may be at the same place with
			 * relocated contents.
			 * Have to find the current run again when this
			 * happens.
			 */
			if ((na->rl != orl) || ((*prl)->lcn != olcn)) {
				zrl = &na->rl[irl];
				while (zrl->length && (zrl->lcn != olcn))
					zrl++;
				*prl = zrl;
			}
			if (!(*prl)->length) {
				 ntfs_log_error("Mapped run not found,"
					" inode %lld lcn 0x%llx\n",
					(long long)na->ni->mft_no,
					(long long)olcn);
				rl = (runlist_element*)NULL;
			} else {
				rl = ntfs_rl_extend(na,*prl,2);
				na->unused_runs = 2;
			}
		}
		*prl = rl;
		if (rl && undecided) {
			allocated = 0;
			zrl = rl;
			irl = 0;
			while (zrl->length && (zrl->lcn >= 0)
			    && (allocated < endblock)) {
				allocated += zrl->length;
				zrl++;
				irl++;
			}
		}
	}
		/*
		 * compression block not fully allocated and followed
		 * by a hole : we must allocate in the hole.
		 */
	if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) {
		s64 xofs;

			/*
			 * split the hole if not fully needed
			 */
		if ((allocated + zrl->length) > endblock) {
			runlist_element *xrl;

			*prl = ntfs_rl_extend(na,*prl,1);
			if (*prl) {
					/* beware : rl was reallocated */
				rl = *prl;
				zrl = &rl[irl];
				na->unused_runs = 0;
				xrl = zrl;
				while (xrl->length) xrl++;
				do {
					xrl[1] = *xrl;
				} while (xrl-- != zrl);
				zrl->length = endblock - allocated;
				zrl[1].length -= zrl->length;
				zrl[1].vcn = zrl->vcn + zrl->length;
			}
		}
		if (*prl) {
			if (wasnonresident)
				compressed_part = na->compression_block_clusters
				   - zrl->length;
			xofs = 0;
			if (ntfs_attr_fill_hole(na,
				    zrl->length << cluster_size_bits,
				    &xofs, &zrl, update_from))
					compressed_part = -1;
			else {
			/* go back to initial cluster, now reallocated */
				while (zrl->vcn > (pos >> cluster_size_bits))
					zrl--;
				*prl = zrl;
			}
		}
	}
	if (!*prl) {
		ntfs_log_error("No elements to borrow from a hole\n");
		compressed_part = -1;
	} else
		if ((*update_from == -1) || ((*prl)->vcn < *update_from))
			*update_from = (*prl)->vcn;
	return (compressed_part);
}

static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize,
				hole_type holes);

/**
 * ntfs_attr_pwrite - positioned write to an ntfs attribute
 * @na:		ntfs attribute to write to
 * @pos:	position in the attribute to write to
 * @count:	number of bytes to write
 * @b:		data buffer to write to disk
 *
 * This function will write @count bytes from data buffer @b to ntfs attribute
 * @na at position @pos.
 *
 * On success, return the number of successfully written bytes. If this number
 * is lower than @count this means that an error was encountered during the
 * write so that the write is partial. 0 means nothing was written (also return
 * 0 when @count is 0).
 *
 * On error and nothing has been written, return -1 with errno set
 * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of
 * invalid arguments.
 */
s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
{
	s64 written, to_write, ofs, old_initialized_size, old_data_size;
	s64 total = 0;
	VCN update_from = -1;
	ntfs_volume *vol;
	s64 fullcount;
	ntfs_attr_search_ctx *ctx = NULL;
	runlist_element *rl;
	s64 hole_end;
	int eo;
	int compressed_part;
	struct {
		unsigned int undo_initialized_size	: 1;
		unsigned int undo_data_size		: 1;
	} need_to = { 0, 0 };
	BOOL wasnonresident = FALSE;
	BOOL compressed;
	BOOL updatemap;

	ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count "
		       "0x%llx.\n", (long long)na->ni->mft_no, na->type,
		       (long long)pos, (long long)count);
	
	if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
		errno = EINVAL;
		ntfs_log_perror("%s", __FUNCTION__);
		goto errno_set;
	}
	vol = na->ni->vol;
	compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
			 != const_cpu_to_le16(0);
	na->unused_runs = 0; /* prepare overflow checks */
	/*
	 * Encrypted attributes are only supported in raw mode.  We return
	 * access denied, which is what Windows NT4 does, too.
	 * Moreover a file cannot be both encrypted and compressed.
	 */
	if ((na->data_flags & ATTR_IS_ENCRYPTED)
	   && (compressed || !vol->efs_raw)) {
		errno = EACCES;
		goto errno_set;
	}
		/*
		 * Fill the gap, when writing beyond the end of a compressed
		 * file. This will make recursive calls
		 */
	if (compressed
	    && (na->type == AT_DATA)
	    && (pos > na->initialized_size)
	    && stuff_hole(na,pos))
		goto errno_set;
	/* If this is a compressed attribute it needs special treatment. */
	wasnonresident = NAttrNonResident(na) != 0;
		/*
		 * Compression is restricted to data streams and
		 * only ATTR_IS_COMPRESSED compression mode is supported.
                 */
	if (compressed
	    && ((na->type != AT_DATA)
		|| ((na->data_flags & ATTR_COMPRESSION_MASK)
			 != ATTR_IS_COMPRESSED))) {
		errno = EOPNOTSUPP;
		goto errno_set;
	}
	
	if (!count)
		goto out;
	/* for a compressed file, get prepared to reserve a full block */
	fullcount = count;
	/* If the write reaches beyond the end, extend the attribute. */
	old_data_size = na->data_size;
	/* identify whether this is appending to a non resident data attribute */
	if ((na->type == AT_DATA) && (pos >= old_data_size)
	    && NAttrNonResident(na))
		NAttrSetDataAppending(na);
	if (pos + count > na->data_size) {
#if PARTIAL_RUNLIST_UPDATING
		/*
		 * When appending data, the attribute is first extended
		 * before being filled with data. This may cause the
		 * attribute to be made temporarily sparse, which
		 * implies reformating the inode and reorganizing the
		 * full runlist. To avoid unnecessary reorganization,
		 * we avoid sparse testing until the data is filled in.
		 */
		if (ntfs_attr_truncate_i(na, pos + count,
					(NAttrDataAppending(na) ?
						HOLES_DELAY : HOLES_OK))) {
			ntfs_log_perror("Failed to enlarge attribute");
			goto errno_set;
		}
		/*
		 * If we avoided updating the runlist, we must be sure
		 * to cancel the enlargement and put back the runlist to
		 * a clean state if we get into some error.
		 */
		if (NAttrDataAppending(na))
			need_to.undo_data_size = 1;
#else
		if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) {
			ntfs_log_perror("Failed to enlarge attribute");
			goto errno_set;
		}
#endif
			/* resizing may change the compression mode */
		compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
				!= const_cpu_to_le16(0);
		need_to.undo_data_size = 1;
	}
		/*
		 * For compressed data, a single full block was allocated
		 * to deal with compression, possibly in a previous call.
		 * We are not able to process several blocks because
		 * some clusters are freed after compression and
		 * new allocations have to be done before proceeding,
		 * so truncate the requested count if needed (big buffers).
		 */
	if (compressed) {
		fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos;
		if (count > fullcount)
			count = fullcount;
	}
	old_initialized_size = na->initialized_size;
	/* If it is a resident attribute, write the data to the mft record. */
	if (!NAttrNonResident(na)) {
		char *val;

		ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
		if (!ctx)
			goto err_out;
		if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
				0, NULL, 0, ctx)) {
			ntfs_log_perror("%s: lookup failed", __FUNCTION__);
			goto err_out;
		}
		val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset);
		if (val < (char*)ctx->attr || val +
				le32_to_cpu(ctx->attr->value_length) >
				(char*)ctx->mrec + vol->mft_record_size) {
			errno = EIO;
			ntfs_log_perror("%s: Sanity check failed", __FUNCTION__);
			goto err_out;
		}
		memcpy(val + pos, b, count);
		if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
				ctx->mrec)) {
			/*
			 * NOTE: We are in a bad state at this moment. We have
			 * dirtied the mft record but we failed to commit it to
			 * disk. Since we have read the mft record ok before,
			 * it is unlikely to fail writing it, so is ok to just
			 * return error here... (AIA)
			 */
			ntfs_log_perror("%s: failed to write mft record", __FUNCTION__);
			goto err_out;
		}
		ntfs_attr_put_search_ctx(ctx);
		total = count;
		goto out;
	}
	
	/* Handle writes beyond initialized_size. */

	if (pos + count > na->initialized_size) {
#if PARTIAL_RUNLIST_UPDATING
		/*
		 * When appending, we only need to map the end of the runlist,
		 * starting at the last previously allocated run, so that
		 * we are able a new one to it.
		 * However, for compressed file, we need the full compression
		 * block, which may be split in several extents.
		 */
		if (NAttrDataAppending(na)) {
			VCN block_begin = pos >> vol->cluster_size_bits;

			if (compressed)
				block_begin &= -na->compression_block_clusters;
			if (block_begin)
				block_begin--;
			if (ntfs_attr_map_partial_runlist(na, block_begin))
				goto err_out;
			if ((update_from == -1) || (block_begin < update_from))
				update_from = block_begin;
		} else
#endif
			if (ntfs_attr_map_whole_runlist(na))
				goto err_out;
		/*
		 * For a compressed attribute, we must be sure there is an
		 * available entry, and, when reopening a compressed file,
		 * we may need to split a hole. So reserve the entries
		 * before it gets too late.
		 */
		if (compressed) {
			na->rl = ntfs_rl_extend(na,na->rl,2);
			if (!na->rl)
				goto err_out;
			na->unused_runs = 2;
		}
		/* Set initialized_size to @pos + @count. */
		ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
		if (!ctx)
			goto err_out;
		if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
				0, NULL, 0, ctx))
			goto err_out;
		
		/* If write starts beyond initialized_size, zero the gap. */
		if (pos > na->initialized_size)
			if (ntfs_attr_fill_zero(na, na->initialized_size, 
						pos - na->initialized_size))
				goto err_out;
			
		ctx->attr->initialized_size = cpu_to_sle64(pos + count);
		/* fix data_size for compressed files */
		if (compressed) {
			na->data_size = pos + count;
			ctx->attr->data_size = ctx->attr->initialized_size;
		}
		if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
				ctx->mrec)) {
			/*
			 * Undo the change in the in-memory copy and send it
			 * back for writing.
			 */
			ctx->attr->initialized_size =
					cpu_to_sle64(old_initialized_size);
			ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
					ctx->mrec);
			goto err_out;
		}
		na->initialized_size = pos + count;
#if CACHE_NIDATA_SIZE
		if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
		    ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
		    : na->type == AT_DATA && na->name == AT_UNNAMED) {
			na->ni->data_size = na->data_size;
			if ((compressed || NAttrSparse(na))
					&& NAttrNonResident(na))
				na->ni->allocated_size = na->compressed_size;
			else
				na->ni->allocated_size = na->allocated_size;
			set_nino_flag(na->ni,KnownSize);
		}
#endif
		ntfs_attr_put_search_ctx(ctx);
		ctx = NULL;
		/*
		 * NOTE: At this point the initialized_size in the mft record
		 * has been updated BUT there is random data on disk thus if
		 * we decide to abort, we MUST change the initialized_size
		 * again.
		 */
		need_to.undo_initialized_size = 1;
	}
	/* Find the runlist element containing the vcn. */
	rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
	if (!rl) {
		/*
		 * If the vcn is not present it is an out of bounds write.
		 * However, we already extended the size of the attribute,
		 * so getting this here must be an error of some kind.
		 */
		if (errno == ENOENT) {
			errno = EIO;
			ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__);
		}
		goto err_out;
	}
		/*
		 * Determine if there is compressed data in the current
		 * compression block (when appending to an existing file).
		 * If so, decompression will be needed, and the full block
		 * must be allocated to be identified as uncompressed.
		 * This comes in two variants, depending on whether
		 * compression has saved at least one cluster.
		 * The compressed size can never be over full size by
		 * more than 485 (maximum for 15 compression blocks
		 * compressed to 4098 and the last 3640 bytes compressed
		 * to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485)
		 * This is less than the smallest cluster, so the hole is
		 * is never beyond the cluster next to the position of
		 * the first uncompressed byte to write.
		 */
	compressed_part = 0;
	if (compressed) {
		if ((rl->lcn == (LCN)LCN_HOLE)
		    && wasnonresident) {
			if (rl->length < na->compression_block_clusters)
				/*
				 * the needed block is in a hole smaller
				 * than the compression block : we can use
				 * it fully
				 */
				compressed_part
					= na->compression_block_clusters
					   - rl->length;
			else {
				/*
				 * the needed block is in a hole bigger
				 * than the compression block : we must
				 * split the hole and use it partially
				 */
				compressed_part = split_compressed_hole(na,
					&rl, pos, count, &update_from);
			}
		} else {
			if (rl->lcn >= 0) {
				/*
				 * the needed block contains data, make
				 * sure the full compression block is
				 * allocated. Borrow from hole if needed
				 */
				compressed_part = borrow_from_hole(na,
					&rl, pos, count, &update_from,
					wasnonresident);
			}
		}

		if (compressed_part < 0)
			goto err_out;

			/* just making non-resident, so not yet compressed */
		if (NAttrBeingNonResident(na)
		    && (compressed_part < na->compression_block_clusters))
			compressed_part = 0;
	}
	ofs = pos - (rl->vcn << vol->cluster_size_bits);
	/*
	 * Scatter the data from the linear data buffer to the volume. Note, a
	 * partial final vcn is taken care of by the @count capping of write
	 * length.
	 */
	for (hole_end = 0; count; rl++, ofs = 0) {
		if (rl->lcn == LCN_RL_NOT_MAPPED) {
			rl = ntfs_attr_find_vcn(na, rl->vcn);
			if (!rl) {
				if (errno == ENOENT) {
					errno = EIO;
					ntfs_log_perror("%s: Failed to find VCN"
							" #4", __FUNCTION__);
				}
				goto rl_err_out;
			}
			/* Needed for case when runs merged. */
			ofs = pos + total - (rl->vcn << vol->cluster_size_bits);
		}
		if (!rl->length) {
			errno = EIO;
			ntfs_log_perror("%s: Zero run length", __FUNCTION__);
			goto rl_err_out;
		}
		if (rl->lcn < (LCN)0) {
			hole_end = rl->vcn + rl->length;

			if (rl->lcn != (LCN)LCN_HOLE) {
				errno = EIO;
				ntfs_log_perror("%s: Unexpected LCN (%lld)", 
						__FUNCTION__,
						(long long)rl->lcn);
				goto rl_err_out;
			}
			if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl,
					 &update_from))
				goto err_out;
		}
		if (compressed) {
			while (rl->length
			    && (ofs >= (rl->length << vol->cluster_size_bits))) {
				ofs -= rl->length << vol->cluster_size_bits;
				rl++;
			}
		}

		/* It is a real lcn, write it to the volume. */
		to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs);
retry:
		ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs "
			       "%lld.\n", (long long)to_write, (long long)rl->vcn,
			       (long long)rl->lcn, (long long)ofs);
		if (!NVolReadOnly(vol)) {
			
			s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs;
			s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write;
			u32 bsize = vol->cluster_size;
			/* Byte size needed to zero fill a cluster */
			s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend;
			/**
			 * Zero fill to cluster boundary if we're writing at the
			 * end of the attribute or into an ex-sparse cluster.
			 * This will cause the kernel not to seek and read disk 
			 * blocks during write(2) to fill the end of the buffer 
			 * which increases write speed by 2-10 fold typically.
			 *
			 * This is done even for compressed files, because
			 * data is generally first written uncompressed.
			 */
			if (rounding && ((wend == na->initialized_size) ||
				(wend < (hole_end << vol->cluster_size_bits)))){
				
				char *cb;
				
				rounding += to_write;
				
				cb = ntfs_malloc(rounding);
				if (!cb)
					goto err_out;
				
				memcpy(cb, b, to_write);
				memset(cb + to_write, 0, rounding - to_write);
				
				if (compressed) {
					written = ntfs_compressed_pwrite(na,
						rl, wpos, ofs, to_write,
						rounding, cb, compressed_part,
						&update_from);
				} else {
					written = ntfs_pwrite(vol->dev, wpos,
						rounding, cb); 
					if (written == rounding)
						written = to_write;
				}
				
				free(cb);
			} else {
				if (compressed) {
					written = ntfs_compressed_pwrite(na,
						rl, wpos, ofs, to_write, 
						to_write, b, compressed_part,
						&update_from);
				} else
					written = ntfs_pwrite(vol->dev, wpos,
						to_write, b);
				}
		} else
			written = to_write;
		/* If everything ok, update progress counters and continue. */
		if (written > 0) {
			total += written;
			count -= written;
			fullcount -= written;
			b = (const u8*)b + written;
		}
		if (written != to_write) {
			/* Partial write cannot be dealt with, stop there */
			/* If the syscall was interrupted, try again. */
			if (written == (s64)-1 && errno == EINTR)
				goto retry;
			if (!written)
				errno = EIO;
			goto rl_err_out;
		}
		compressed_part = 0;
	}
done:
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
		/*
		 *	 Update mapping pairs if needed.
		 * For a compressed file, we try to make a partial update
		 * of the mapping list. This makes a difference only if
		 * inode extents were needed.
		 */
#if PARTIAL_RUNLIST_UPDATING
	updatemap = NAttrFullyMapped(na) || NAttrDataAppending(na);
#else
	updatemap = (compressed
			? NAttrFullyMapped(na) != 0 : update_from != -1);
#endif
	if (updatemap) {
		if (ntfs_attr_update_mapping_pairs(na,
				(update_from < 0 ? 0 : update_from))) {
			/*
			 * FIXME: trying to recover by goto rl_err_out; 
			 * could cause driver hang by infinite looping.
			 */
			total = -1;
			goto out;
		}
		if (!wasnonresident)
			NAttrClearBeingNonResident(na);
		NAttrClearDataAppending(na);
	}
out:	
	ntfs_log_leave("\n");
	return total;
rl_err_out:
	eo = errno;
	if (total) {
		if (need_to.undo_initialized_size) {
			if (pos + total > na->initialized_size)
				goto done;
			/*
			 * TODO: Need to try to change initialized_size. If it
			 * succeeds goto done, otherwise goto err_out. (AIA)
			 */
			goto err_out;
		}
		goto done;
	}
	errno = eo;
err_out:
	eo = errno;
	if (need_to.undo_initialized_size) {
		int err;

		err = 0;
		if (!ctx) {
			ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
			if (!ctx)
				err = 1;
		} else
			ntfs_attr_reinit_search_ctx(ctx);
		if (!err) {
			err = ntfs_attr_lookup(na->type, na->name,
					na->name_len, 0, 0, NULL, 0, ctx);
			if (!err) {
				na->initialized_size = old_initialized_size;
				ctx->attr->initialized_size = cpu_to_sle64(
						old_initialized_size);
				err = ntfs_mft_record_write(vol,
						ctx->ntfs_ino->mft_no,
						ctx->mrec);
			}
		}
		if (err) {
			/*
			 * FIXME: At this stage could try to recover by filling
			 * old_initialized_size -> new_initialized_size with
			 * data or at least zeroes. (AIA)
			 */
			ntfs_log_error("Eeek! Failed to recover from error. "
					"Leaving metadata in inconsistent "
					"state! Run chkdsk!\n");
		}
	}
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
	/* Update mapping pairs if needed. */
	updatemap = (compressed
			? NAttrFullyMapped(na) != 0 : update_from != -1);
	if (updatemap)
		ntfs_attr_update_mapping_pairs(na, 0);
	/* Restore original data_size if needed. */
	if (need_to.undo_data_size
			&& ntfs_attr_truncate_i(na, old_data_size, HOLES_OK))
		ntfs_log_perror("Failed to restore data_size");
	errno = eo;
errno_set:
	total = -1;
	goto out;
}

int ntfs_attr_pclose(ntfs_attr *na)
{
	s64 ofs;
	int failed;
	BOOL ok = TRUE;
	VCN update_from = -1;
	ntfs_volume *vol;
	ntfs_attr_search_ctx *ctx = NULL;
	runlist_element *rl;
	int eo;
	int compressed_part;
	BOOL compressed;

	ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n",
			na->ni->mft_no, na->type);
	
	if (!na || !na->ni || !na->ni->vol) {
		errno = EINVAL;
		ntfs_log_perror("%s", __FUNCTION__);
		goto errno_set;
	}
	vol = na->ni->vol;
	na->unused_runs = 0;
	compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
			 != const_cpu_to_le16(0);
	/*
	 * Encrypted non-resident attributes are not supported.  We return
	 * access denied, which is what Windows NT4 does, too.
	 */
	if (NAttrEncrypted(na) && NAttrNonResident(na)) {
		errno = EACCES;
		goto errno_set;
	}
	/* If this is not a compressed attribute get out */
	/* same if it is resident */
	if (!compressed || !NAttrNonResident(na))
		goto out;

		/* safety check : no recursion on close */
	if (NAttrComprClosing(na)) {
		errno = EIO;
		ntfs_log_error("Bad ntfs_attr_pclose"
				" recursion on inode %lld\n",
				(long long)na->ni->mft_no);
		goto out;
	}
	NAttrSetComprClosing(na);
		/*
		 * For a compressed attribute, we must be sure there are two
		 * available entries, so reserve them before it gets too late.
		 */
	if (ntfs_attr_map_whole_runlist(na))
		goto err_out;
	na->rl = ntfs_rl_extend(na,na->rl,2);
	if (!na->rl)
		goto err_out;
	na->unused_runs = 2;
	/* Find the runlist element containing the terminal vcn. */
	rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits);
	if (!rl) {
		/*
		 * If the vcn is not present it is an out of bounds write.
		 * However, we have already written the last byte uncompressed,
		 * so getting this here must be an error of some kind.
		 */
		if (errno == ENOENT) {
			errno = EIO;
			ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__);
		}
		goto err_out;
	}
	/*
	 * Scatter the data from the linear data buffer to the volume. Note, a
	 * partial final vcn is taken care of by the @count capping of write
	 * length.
	 */
	compressed_part = 0;
 	if (rl->lcn >= 0) {
		runlist_element *xrl;

		xrl = rl;
		do {
			xrl++;
		} while (xrl->lcn >= 0);
		compressed_part = (-xrl->length)
					& (na->compression_block_clusters - 1);
	} else
		if (rl->lcn == (LCN)LCN_HOLE) {
			if (rl->length < na->compression_block_clusters)
				compressed_part
        	                        = na->compression_block_clusters
                	                           - rl->length;
			else
				compressed_part
					= na->compression_block_clusters;
		}
		/* done, if the last block set was compressed */
	if (compressed_part)
		goto out;

	ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits);

	if (rl->lcn == LCN_RL_NOT_MAPPED) {
		rl = ntfs_attr_find_vcn(na, rl->vcn);
		if (!rl) {
			if (errno == ENOENT) {
				errno = EIO;
				ntfs_log_perror("%s: Failed to find VCN"
						" #6", __FUNCTION__);
			}
			goto rl_err_out;
		}
			/* Needed for case when runs merged. */
		ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits);
	}
	if (!rl->length) {
		errno = EIO;
		ntfs_log_perror("%s: Zero run length", __FUNCTION__);
		goto rl_err_out;
	}
	if (rl->lcn < (LCN)0) {
		if (rl->lcn != (LCN)LCN_HOLE) {
			errno = EIO;
			ntfs_log_perror("%s: Unexpected LCN (%lld)", 
					__FUNCTION__,
					(long long)rl->lcn);
			goto rl_err_out;
		}
			
		if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from))
			goto err_out;
	}
	while (rl->length
	    && (ofs >= (rl->length << vol->cluster_size_bits))) {
		ofs -= rl->length << vol->cluster_size_bits;
		rl++;
	}

retry:
	failed = 0;
	if (update_from < 0) update_from = 0;
	if (!NVolReadOnly(vol)) {
		failed = ntfs_compressed_close(na, rl, ofs, &update_from);
#if CACHE_NIDATA_SIZE
		if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
		    ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
		    : na->type == AT_DATA && na->name == AT_UNNAMED) {
			na->ni->data_size = na->data_size;
			na->ni->allocated_size = na->compressed_size;
			set_nino_flag(na->ni,KnownSize);
		}
#endif
	}
	if (failed) {
		/* If the syscall was interrupted, try again. */
		if (errno == EINTR)
			goto retry;
		else
			goto rl_err_out;
	}
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
	/* Update mapping pairs if needed. */
	if (NAttrFullyMapped(na))
		if (ntfs_attr_update_mapping_pairs(na, update_from)) {
			/*
			 * FIXME: trying to recover by goto rl_err_out; 
			 * could cause driver hang by infinite looping.
			 */
			ok = FALSE;
			goto out;
	}
out:	
	NAttrClearComprClosing(na);
	ntfs_log_leave("\n");
	return (!ok);
rl_err_out:
		/*
		 * need not restore old sizes, only compressed_size
		 * can have changed. It has been set according to
		 * the current runlist while updating the mapping pairs,
		 * and must be kept consistent with the runlists.
		 */
err_out:
	eo = errno;
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
	/* Update mapping pairs if needed. */
	if (NAttrFullyMapped(na))
		ntfs_attr_update_mapping_pairs(na, 0);
	errno = eo;
errno_set:
	ok = FALSE;
	goto out;
}

/**
 * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read
 * @na:		multi sector transfer protected ntfs attribute to read from
 * @pos:	byte position in the attribute to begin reading from
 * @bk_cnt:	number of mst protected blocks to read
 * @bk_size:	size of each mst protected block in bytes
 * @dst:	output data buffer
 *
 * This function will read @bk_cnt blocks of size @bk_size bytes each starting
 * at offset @pos from the ntfs attribute @na into the data buffer @b.
 *
 * On success, the multi sector transfer fixups are applied and the number of
 * read blocks is returned. If this number is lower than @bk_cnt this means
 * that the read has either reached end of attribute or that an error was
 * encountered during the read so that the read is partial. 0 means end of
 * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0).
 *
 * On error and nothing has been read, return -1 with errno set appropriately
 * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid
 * arguments.
 *
 * NOTE: If an incomplete multi sector transfer is detected the magic is
 * changed to BAAD but no error is returned, i.e. it is possible that any of
 * the returned blocks have multi sector transfer errors. This should be
 * detected by the caller by checking each block with is_baad_recordp(&block).
 * The reasoning is that we want to fixup as many blocks as possible and we
 * want to return even bad ones to the caller so, e.g. in case of ntfsck, the
 * errors can be repaired.
 */
s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt,
		const u32 bk_size, void *dst)
{
	s64 br;
	u8 *end;
	BOOL warn;

	ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n",
			(unsigned long long)na->ni->mft_no, na->type,
			(long long)pos);
	if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) {
		errno = EINVAL;
		ntfs_log_perror("%s", __FUNCTION__);
		return -1;
	}
	br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst);
	if (br <= 0)
		return br;
	br /= bk_size;
		/* log errors unless silenced */
	warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol);
	for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst +
			bk_size)
		ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn);
	/* Finally, return the number of blocks read. */
	return br;
}

/**
 * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write
 * @na:		multi sector transfer protected ntfs attribute to write to
 * @pos:	position in the attribute to write to
 * @bk_cnt:	number of mst protected blocks to write
 * @bk_size:	size of each mst protected block in bytes
 * @src:	data buffer to write to disk
 *
 * This function will write @bk_cnt blocks of size @bk_size bytes each from
 * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na
 * at position @pos.
 *
 * On success, return the number of successfully written blocks. If this number
 * is lower than @bk_cnt this means that an error was encountered during the
 * write so that the write is partial. 0 means nothing was written (also
 * return 0 when @bk_cnt or @bk_size are 0).
 *
 * On error and nothing has been written, return -1 with errno set
 * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case
 * of invalid arguments.
 *
 * NOTE: We mst protect the data, write it, then mst deprotect it using a quick
 * deprotect algorithm (no checking). This saves us from making a copy before
 * the write and at the same time causes the usn to be incremented in the
 * buffer. This conceptually fits in better with the idea that cached data is
 * always deprotected and protection is performed when the data is actually
 * going to hit the disk and the cache is immediately deprotected again
 * simulating an mst read on the written data. This way cache coherency is
 * achieved.
 */
s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt,
		const u32 bk_size, void *src)
{
	s64 written, i;

	ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n",
			(unsigned long long)na->ni->mft_no, na->type,
			(long long)pos);
	if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) {
		errno = EINVAL;
		return -1;
	}
	if (!bk_cnt)
		return 0;
	/* Prepare data for writing. */
	for (i = 0; i < bk_cnt; ++i) {
		int err;

		err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
				((u8*)src + i * bk_size), bk_size);
		if (err < 0) {
			/* Abort write at this position. */
			ntfs_log_perror("%s #1", __FUNCTION__);
			if (!i)
				return err;
			bk_cnt = i;
			break;
		}
	}
	/* Write the prepared data. */
	written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src);
	if (written <= 0) {
		ntfs_log_perror("%s: written=%lld", __FUNCTION__,
				(long long)written);
	}
	/* Quickly deprotect the data again. */
	for (i = 0; i < bk_cnt; ++i)
		ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i *
				bk_size));
	if (written <= 0)
		return written;
	/* Finally, return the number of complete blocks written. */
	return written / bk_size;
}

/**
 * ntfs_attr_find - find (next) attribute in mft record
 * @type:	attribute type to find
 * @name:	attribute name to find (optional, i.e. NULL means don't care)
 * @name_len:	attribute name length (only needed if @name present)
 * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
 * @val:	attribute value to find (optional, resident attributes only)
 * @val_len:	attribute value length
 * @ctx:	search context with mft record and attribute to search from
 *
 * You shouldn't need to call this function directly. Use lookup_attr() instead.
 *
 * ntfs_attr_find() takes a search context @ctx as parameter and searches the
 * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an
 * attribute of @type, optionally @name and @val. If found, ntfs_attr_find()
 * returns 0 and @ctx->attr will point to the found attribute.
 *
 * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and
 * @ctx->attr will point to the attribute before which the attribute being
 * searched for would need to be inserted if such an action were to be desired.
 *
 * On actual error, ntfs_attr_find() returns -1 with errno set to the error
 * code but not to ENOENT.  In this case @ctx->attr is undefined and in
 * particular do not rely on it not changing.
 *
 * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it
 * is FALSE, the search begins after @ctx->attr.
 *
 * If @type is AT_UNUSED, return the first found attribute, i.e. one can
 * enumerate all attributes by setting @type to AT_UNUSED and then calling
 * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to
 * indicate that there are no more entries. During the enumeration, each
 * successful call of ntfs_attr_find() will return the next attribute in the
 * mft record @ctx->mrec.
 *
 * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT.
 * AT_END is not a valid attribute, its length is zero for example, thus it is
 * safer to return error instead of success in this case. This also allows us
 * to interoperate cleanly with ntfs_external_attr_find().
 *
 * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
 * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
 * match both named and unnamed attributes.
 *
 * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and
 * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record
 * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at
 * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case
 * sensitive. When @name is present, @name_len is the @name length in Unicode
 * characters.
 *
 * If @name is not present (NULL), we assume that the unnamed attribute is
 * being searched for.
 *
 * Finally, the resident attribute value @val is looked for, if present.
 * If @val is not present (NULL), @val_len is ignored.
 *
 * ntfs_attr_find() only searches the specified mft record and it ignores the
 * presence of an attribute list attribute (unless it is the one being searched
 * for, obviously). If you need to take attribute lists into consideration, use
 * ntfs_attr_lookup() instead (see below). This also means that you cannot use
 * ntfs_attr_find() to search for extent records of non-resident attributes, as
 * extents with lowest_vcn != 0 are usually described by the attribute list
 * attribute only. - Note that it is possible that the first extent is only in
 * the attribute list while the last extent is in the base mft record, so don't
 * rely on being able to find the first extent in the base mft record.
 *
 * Warning: Never use @val when looking for attribute types which can be
 *	    non-resident as this most likely will result in a crash!
 */
static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
		const u32 name_len, const IGNORE_CASE_BOOL ic,
		const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx)
{
	ATTR_RECORD *a;
	ntfs_volume *vol;
	ntfschar *upcase;
	u32 upcase_len;

	ntfs_log_trace("attribute type 0x%x.\n", type);

	if (ctx->ntfs_ino) {
		vol = ctx->ntfs_ino->vol;
		upcase = vol->upcase;
		upcase_len = vol->upcase_len;
	} else {
		if (name && name != AT_UNNAMED) {
			errno = EINVAL;
			ntfs_log_perror("%s", __FUNCTION__);
			return -1;
		}
		vol = NULL;
		upcase = NULL;
		upcase_len = 0;
	}
	/*
	 * Iterate over attributes in mft record starting at @ctx->attr, or the
	 * attribute following that, if @ctx->is_first is TRUE.
	 */
	if (ctx->is_first) {
		a = ctx->attr;
		ctx->is_first = FALSE;
	} else
		a = (ATTR_RECORD*)((char*)ctx->attr +
				le32_to_cpu(ctx->attr->length));
	for (;;	a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) {
		if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec +
				le32_to_cpu(ctx->mrec->bytes_allocated))
			break;
		ctx->attr = a;
		if (((type != AT_UNUSED) && (le32_to_cpu(a->type) >
				le32_to_cpu(type))) ||
				(a->type == AT_END)) {
			errno = ENOENT;
			return -1;
		}
		if (!a->length)
			break;
		/* If this is an enumeration return this attribute. */
		if (type == AT_UNUSED)
			return 0;
		if (a->type != type)
			continue;
		/*
		 * If @name is AT_UNNAMED we want an unnamed attribute.
		 * If @name is present, compare the two names.
		 * Otherwise, match any attribute.
		 */
		if (name == AT_UNNAMED) {
			/* The search failed if the found attribute is named. */
			if (a->name_length) {
				errno = ENOENT;
				return -1;
			}
		} else {
			register int rc;
			if (name && ((rc = ntfs_names_full_collate(name,
					name_len, (ntfschar*)((char*)a +
						le16_to_cpu(a->name_offset)),
					a->name_length, ic,
					upcase, upcase_len)))) {
				/*
				 * If @name collates before a->name,
				 * there is no matching attribute.
				 */
				if (rc < 0) {
					errno = ENOENT;
					return -1;
				}
			/* If the strings are not equal, continue search. */
			continue;
			}
		}
		/*
		 * The names match or @name not present and attribute is
		 * unnamed. If no @val specified, we have found the attribute
		 * and are done.
		 */
		if (!val)
			return 0;
		/* @val is present; compare values. */
		else {
			register int rc;

			rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset),
					min(val_len,
					le32_to_cpu(a->value_length)));
			/*
			 * If @val collates before the current attribute's
			 * value, there is no matching attribute.
			 */
			if (!rc) {
				register u32 avl;
				avl = le32_to_cpu(a->value_length);
				if (val_len == avl)
					return 0;
				if (val_len < avl) {
					errno = ENOENT;
					return -1;
				}
			} else if (rc < 0) {
				errno = ENOENT;
				return -1;
			}
		}
	}
	errno = EIO;
	ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, 
			ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1);
	return -1;
}

void ntfs_attr_name_free(char **name)
{
	if (*name) {
		free(*name);
		*name = NULL;
	}
}

char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len)
{
	char *name = NULL;
	int name_len;

	name_len = ntfs_ucstombs(uname, uname_len, &name, 0);
	if (name_len < 0) {
		ntfs_log_perror("ntfs_ucstombs");
		return NULL;

	} else if (name_len > 0)
		return name;

	ntfs_attr_name_free(&name);
	return NULL;
}

/**
 * ntfs_external_attr_find - find an attribute in the attribute list of an inode
 * @type:	attribute type to find
 * @name:	attribute name to find (optional, i.e. NULL means don't care)
 * @name_len:	attribute name length (only needed if @name present)
 * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
 * @lowest_vcn:	lowest vcn to find (optional, non-resident attributes only)
 * @val:	attribute value to find (optional, resident attributes only)
 * @val_len:	attribute value length
 * @ctx:	search context with mft record and attribute to search from
 *
 * You shouldn't need to call this function directly. Use ntfs_attr_lookup()
 * instead.
 *
 * Find an attribute by searching the attribute list for the corresponding
 * attribute list entry. Having found the entry, map the mft record for read
 * if the attribute is in a different mft record/inode, find the attribute in
 * there and return it.
 *
 * If @type is AT_UNUSED, return the first found attribute, i.e. one can
 * enumerate all attributes by setting @type to AT_UNUSED and then calling
 * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to
 * ENOENT to indicate that there are no more entries. During the enumeration,
 * each successful call of ntfs_external_attr_find() will return the next
 * attribute described by the attribute list of the base mft record described
 * by the search context @ctx.
 *
 * If @type is AT_END, seek to the end of the base mft record ignoring the
 * attribute list completely and return -1 with errno set to ENOENT.  AT_END is
 * not a valid attribute, its length is zero for example, thus it is safer to
 * return error instead of success in this case.
 *
 * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
 * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
 * match both named and unnamed attributes.
 *
 * On first search @ctx->ntfs_ino must be the inode of the base mft record and
 * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx().
 * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too
 * (@ctx->base_ntfs_ino is then the base inode).
 *
 * After finishing with the attribute/mft record you need to call
 * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
 * mapped extent inodes, etc).
 *
 * Return 0 if the search was successful and -1 if not, with errno set to the
 * error code.
 *
 * On success, @ctx->attr is the found attribute, it is in mft record
 * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this
 * attribute with @ctx->base_* being the base mft record to which @ctx->attr
 * belongs.
 *
 * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the
 * attribute which collates just after the attribute being searched for in the
 * base ntfs inode, i.e. if one wants to add the attribute to the mft record
 * this is the correct place to insert it into, and if there is not enough
 * space, the attribute should be placed in an extent mft record.
 * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list
 * at which the new attribute's attribute list entry should be inserted.  The
 * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL.
 * The only exception to this is when @type is AT_END, in which case
 * @ctx->al_entry is set to NULL also (see above).
 *
 * The following error codes are defined:
 *	ENOENT	Attribute not found, not an error as such.
 *	EINVAL	Invalid arguments.
 *	EIO	I/O error or corrupt data structures found.
 *	ENOMEM	Not enough memory to allocate necessary buffers.
 */
static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
		const u32 name_len, const IGNORE_CASE_BOOL ic,
		const VCN lowest_vcn, const u8 *val, const u32 val_len,
		ntfs_attr_search_ctx *ctx)
{
	ntfs_inode *base_ni, *ni;
	ntfs_volume *vol;
	ATTR_LIST_ENTRY *al_entry, *next_al_entry;
	u8 *al_start, *al_end;
	ATTR_RECORD *a;
	ntfschar *al_name;
	u32 al_name_len;
	BOOL is_first_search = FALSE;

	ni = ctx->ntfs_ino;
	base_ni = ctx->base_ntfs_ino;
	ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n",
			(unsigned long long)ni->mft_no, type);
	if (!base_ni) {
		/* First call happens with the base mft record. */
		base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino;
		ctx->base_mrec = ctx->mrec;
	}
	if (ni == base_ni)
		ctx->base_attr = ctx->attr;
	if (type == AT_END)
		goto not_found;
	vol = base_ni->vol;
	al_start = base_ni->attr_list;
	al_end = al_start + base_ni->attr_list_size;
	if (!ctx->al_entry) {
		ctx->al_entry = (ATTR_LIST_ENTRY*)al_start;
		is_first_search = TRUE;
	}
	/*
	 * Iterate over entries in attribute list starting at @ctx->al_entry,
	 * or the entry following that, if @ctx->is_first is TRUE.
	 */
	if (ctx->is_first) {
		al_entry = ctx->al_entry;
		ctx->is_first = FALSE;
		/*
		 * If an enumeration and the first attribute is higher than
		 * the attribute list itself, need to return the attribute list
		 * attribute.
		 */
		if ((type == AT_UNUSED) && is_first_search &&
				le32_to_cpu(al_entry->type) >
				le32_to_cpu(AT_ATTRIBUTE_LIST))
			goto find_attr_list_attr;
	} else {
		al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +
				le16_to_cpu(ctx->al_entry->length));
		/*
		 * If this is an enumeration and the attribute list attribute
		 * is the next one in the enumeration sequence, just return the
		 * attribute list attribute from the base mft record as it is
		 * not listed in the attribute list itself.
		 */
		if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) <
				le32_to_cpu(AT_ATTRIBUTE_LIST) &&
				le32_to_cpu(al_entry->type) >
				le32_to_cpu(AT_ATTRIBUTE_LIST)) {
			int rc;
find_attr_list_attr:

			/* Check for bogus calls. */
			if (name || name_len || val || val_len || lowest_vcn) {
				errno = EINVAL;
				ntfs_log_perror("%s", __FUNCTION__);
				return -1;
			}

			/* We want the base record. */
			ctx->ntfs_ino = base_ni;
			ctx->mrec = ctx->base_mrec;
			ctx->is_first = TRUE;
			/* Sanity checks are performed elsewhere. */
			ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
					le16_to_cpu(ctx->mrec->attrs_offset));

			/* Find the attribute list attribute. */
			rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0,
					IGNORE_CASE, NULL, 0, ctx);

			/*
			 * Setup the search context so the correct
			 * attribute is returned next time round.
			 */
			ctx->al_entry = al_entry;
			ctx->is_first = TRUE;

			/* Got it. Done. */
			if (!rc)
				return 0;

			/* Error! If other than not found return it. */
			if (errno != ENOENT)
				return rc;

			/* Not found?!? Absurd! */
			errno = EIO;
			ntfs_log_error("Attribute list wasn't found");
			return -1;
		}
	}
	for (;; al_entry = next_al_entry) {
		/* Out of bounds check. */
		if ((u8*)al_entry < base_ni->attr_list ||
				(u8*)al_entry > al_end)
			break;	/* Inode is corrupt. */
		ctx->al_entry = al_entry;
		/* Catch the end of the attribute list. */
		if ((u8*)al_entry == al_end)
			goto not_found;
		if (!al_entry->length)
			break;
		if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
				le16_to_cpu(al_entry->length) > al_end)
			break;
		next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
				le16_to_cpu(al_entry->length));
		if (type != AT_UNUSED) {
			if (le32_to_cpu(al_entry->type) > le32_to_cpu(type))
				goto not_found;
			if (type != al_entry->type)
				continue;
		}
		al_name_len = al_entry->name_length;
		al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset);
		/*
		 * If !@type we want the attribute represented by this
		 * attribute list entry.
		 */
		if (type == AT_UNUSED)
			goto is_enumeration;
		/*
		 * If @name is AT_UNNAMED we want an unnamed attribute.
		 * If @name is present, compare the two names.
		 * Otherwise, match any attribute.
		 */
		if (name == AT_UNNAMED) {
			if (al_name_len)
				goto not_found;
		} else {
			int rc;

			if (name && ((rc = ntfs_names_full_collate(name,
					name_len, al_name, al_name_len, ic,
					vol->upcase, vol->upcase_len)))) {

				/*
				 * If @name collates before al_name,
				 * there is no matching attribute.
				 */
				if (rc < 0)
					goto not_found;
				/* If the strings are not equal, continue search. */
				continue;
			}
		}
		/*
		 * The names match or @name not present and attribute is
		 * unnamed. Now check @lowest_vcn. Continue search if the
		 * next attribute list entry still fits @lowest_vcn. Otherwise
		 * we have reached the right one or the search has failed.
		 */
		if (lowest_vcn && (u8*)next_al_entry >= al_start	    &&
				(u8*)next_al_entry + 6 < al_end	    &&
				(u8*)next_al_entry + le16_to_cpu(
					next_al_entry->length) <= al_end    &&
				sle64_to_cpu(next_al_entry->lowest_vcn) <=
					lowest_vcn			    &&
				next_al_entry->type == al_entry->type	    &&
				next_al_entry->name_length == al_name_len   &&
				ntfs_names_are_equal((ntfschar*)((char*)
					next_al_entry +
					next_al_entry->name_offset),
					next_al_entry->name_length,
					al_name, al_name_len, CASE_SENSITIVE,
					vol->upcase, vol->upcase_len))
			continue;
is_enumeration:
		if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {
			if (MSEQNO_LE(al_entry->mft_reference) !=
					le16_to_cpu(
					ni->mrec->sequence_number)) {
				ntfs_log_error("Found stale mft reference in "
						"attribute list!\n");
				break;
			}
		} else { /* Mft references do not match. */
			/* Do we want the base record back? */
			if (MREF_LE(al_entry->mft_reference) ==
					base_ni->mft_no) {
				ni = ctx->ntfs_ino = base_ni;
				ctx->mrec = ctx->base_mrec;
			} else {
				/* We want an extent record. */
				ni = ntfs_extent_inode_open(base_ni,
						al_entry->mft_reference);
				if (!ni)
					break;
				ctx->ntfs_ino = ni;
				ctx->mrec = ni->mrec;
			}
		}
		a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec +
				le16_to_cpu(ctx->mrec->attrs_offset));
		/*
		 * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the
		 * mft record containing the attribute represented by the
		 * current al_entry.
		 *
		 * We could call into ntfs_attr_find() to find the right
		 * attribute in this mft record but this would be less
		 * efficient and not quite accurate as ntfs_attr_find() ignores
		 * the attribute instance numbers for example which become
		 * important when one plays with attribute lists. Also, because
		 * a proper match has been found in the attribute list entry
		 * above, the comparison can now be optimized. So it is worth
		 * re-implementing a simplified ntfs_attr_find() here.
		 *
		 * Use a manual loop so we can still use break and continue
		 * with the same meanings as above.
		 */
do_next_attr_loop:
		if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec +
				le32_to_cpu(ctx->mrec->bytes_allocated))
			break;
		if (a->type == AT_END)
			continue;
		if (!a->length)
			break;
		if (al_entry->instance != a->instance)
			goto do_next_attr;
		/*
		 * If the type and/or the name are/is mismatched between the
		 * attribute list entry and the attribute record, there is
		 * corruption so we break and return error EIO.
		 */
		if (al_entry->type != a->type)
			break;
		if (!ntfs_names_are_equal((ntfschar*)((char*)a +
				le16_to_cpu(a->name_offset)),
				a->name_length, al_name,
				al_name_len, CASE_SENSITIVE,
				vol->upcase, vol->upcase_len))
			break;
		ctx->attr = a;
		/*
		 * If no @val specified or @val specified and it matches, we
		 * have found it! Also, if !@type, it is an enumeration, so we
		 * want the current attribute.
		 */
		if ((type == AT_UNUSED) || !val || (!a->non_resident &&
				le32_to_cpu(a->value_length) == val_len &&
				!memcmp((char*)a + le16_to_cpu(a->value_offset),
				val, val_len))) {
			return 0;
		}
do_next_attr:
		/* Proceed to the next attribute in the current mft record. */
		a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
		goto do_next_attr_loop;
	}
	if (ni != base_ni) {
		ctx->ntfs_ino = base_ni;
		ctx->mrec = ctx->base_mrec;
		ctx->attr = ctx->base_attr;
	}
	errno = EIO;
	ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no);
	return -1;
not_found:
	/*
	 * If we were looking for AT_END or we were enumerating and reached the
	 * end, we reset the search context @ctx and use ntfs_attr_find() to
	 * seek to the end of the base mft record.
	 */
	if (type == AT_UNUSED || type == AT_END) {
		ntfs_attr_reinit_search_ctx(ctx);
		return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len,
				ctx);
	}
	/*
	 * The attribute wasn't found.  Before we return, we want to ensure
	 * @ctx->mrec and @ctx->attr indicate the position at which the
	 * attribute should be inserted in the base mft record.  Since we also
	 * want to preserve @ctx->al_entry we cannot reinitialize the search
	 * context using ntfs_attr_reinit_search_ctx() as this would set
	 * @ctx->al_entry to NULL.  Thus we do the necessary bits manually (see
	 * ntfs_attr_init_search_ctx() below).  Note, we _only_ preserve
	 * @ctx->al_entry as the remaining fields (base_*) are identical to
	 * their non base_ counterparts and we cannot set @ctx->base_attr
	 * correctly yet as we do not know what @ctx->attr will be set to by
	 * the call to ntfs_attr_find() below.
	 */
	ctx->mrec = ctx->base_mrec;
	ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
			le16_to_cpu(ctx->mrec->attrs_offset));
	ctx->is_first = TRUE;
	ctx->ntfs_ino = ctx->base_ntfs_ino;
	ctx->base_ntfs_ino = NULL;
	ctx->base_mrec = NULL;
	ctx->base_attr = NULL;
	/*
	 * In case there are multiple matches in the base mft record, need to
	 * keep enumerating until we get an attribute not found response (or
	 * another error), otherwise we would keep returning the same attribute
	 * over and over again and all programs using us for enumeration would
	 * lock up in a tight loop.
	 */
	{
		int ret;

		do {
			ret = ntfs_attr_find(type, name, name_len, ic, val,
					val_len, ctx);
		} while (!ret);
		return ret;
	}
}

/**
 * ntfs_attr_lookup - find an attribute in an ntfs inode
 * @type:	attribute type to find
 * @name:	attribute name to find (optional, i.e. NULL means don't care)
 * @name_len:	attribute name length (only needed if @name present)
 * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
 * @lowest_vcn:	lowest vcn to find (optional, non-resident attributes only)
 * @val:	attribute value to find (optional, resident attributes only)
 * @val_len:	attribute value length
 * @ctx:	search context with mft record and attribute to search from
 *
 * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must
 * be the base mft record and @ctx must have been obtained from a call to
 * ntfs_attr_get_search_ctx().
 *
 * This function transparently handles attribute lists and @ctx is used to
 * continue searches where they were left off at.
 *
 * If @type is AT_UNUSED, return the first found attribute, i.e. one can
 * enumerate all attributes by setting @type to AT_UNUSED and then calling
 * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT
 * to indicate that there are no more entries. During the enumeration, each
 * successful call of ntfs_attr_lookup() will return the next attribute, with
 * the current attribute being described by the search context @ctx.
 *
 * If @type is AT_END, seek to the end of the base mft record ignoring the
 * attribute list completely and return -1 with errno set to ENOENT.  AT_END is
 * not a valid attribute, its length is zero for example, thus it is safer to
 * return error instead of success in this case.  It should never be needed to
 * do this, but we implement the functionality because it allows for simpler
 * code inside ntfs_external_attr_find().
 *
 * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
 * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
 * match both named and unnamed attributes.
 *
 * After finishing with the attribute/mft record you need to call
 * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
 * mapped extent inodes, etc).
 *
 * Return 0 if the search was successful and -1 if not, with errno set to the
 * error code.
 *
 * On success, @ctx->attr is the found attribute, it is in mft record
 * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this
 * attribute with @ctx->base_* being the base mft record to which @ctx->attr
 * belongs.  If no attribute list attribute is present @ctx->al_entry and
 * @ctx->base_* are NULL.
 *
 * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the
 * attribute which collates just after the attribute being searched for in the
 * base ntfs inode, i.e. if one wants to add the attribute to the mft record
 * this is the correct place to insert it into, and if there is not enough
 * space, the attribute should be placed in an extent mft record.
 * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list
 * at which the new attribute's attribute list entry should be inserted.  The
 * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL.
 * The only exception to this is when @type is AT_END, in which case
 * @ctx->al_entry is set to NULL also (see above).
 *
 *
 * The following error codes are defined:
 *	ENOENT	Attribute not found, not an error as such.
 *	EINVAL	Invalid arguments.
 *	EIO	I/O error or corrupt data structures found.
 *	ENOMEM	Not enough memory to allocate necessary buffers.
 */
int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
		const u32 name_len, const IGNORE_CASE_BOOL ic,
		const VCN lowest_vcn, const u8 *val, const u32 val_len,
		ntfs_attr_search_ctx *ctx)
{
	ntfs_volume *vol;
	ntfs_inode *base_ni;
	int ret = -1;

	ntfs_log_enter("Entering for attribute type 0x%x\n", type);
	
	if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED &&
			(!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) ||
			!vol->upcase || !vol->upcase_len))) {
		errno = EINVAL;
		ntfs_log_perror("%s", __FUNCTION__);
		goto out;
	}
	
	if (ctx->base_ntfs_ino)
		base_ni = ctx->base_ntfs_ino;
	else
		base_ni = ctx->ntfs_ino;
	if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST)
		ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx);
	else
		ret = ntfs_external_attr_find(type, name, name_len, ic, 
					      lowest_vcn, val, val_len, ctx);
out:
	ntfs_log_leave("\n");
	return ret;
}

/**
 * ntfs_attr_position - find given or next attribute type in an ntfs inode
 * @type:	attribute type to start lookup
 * @ctx:	search context with mft record and attribute to search from
 *
 * Find an attribute type in an ntfs inode or the next attribute which is not
 * the AT_END attribute. Please see more details at ntfs_attr_lookup.
 *
 * Return 0 if the search was successful and -1 if not, with errno set to the
 * error code.
 *
 * The following error codes are defined:
 *	EINVAL	Invalid arguments.
 *	EIO	I/O error or corrupt data structures found.
 *	ENOMEM	Not enough memory to allocate necessary buffers.
 * 	ENOSPC  No attribute was found after 'type', only AT_END.
 */
int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
{
	if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
		if (errno != ENOENT)
			return -1;
		if (ctx->attr->type == AT_END) {
			errno = ENOSPC;
			return -1;
		}
	}
	return 0;
}

/**
 * ntfs_attr_init_search_ctx - initialize an attribute search context
 * @ctx:	attribute search context to initialize
 * @ni:		ntfs inode with which to initialize the search context
 * @mrec:	mft record with which to initialize the search context
 *
 * Initialize the attribute search context @ctx with @ni and @mrec.
 */
static void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx,
		ntfs_inode *ni, MFT_RECORD *mrec)
{
	if (!mrec)
		mrec = ni->mrec;
	ctx->mrec = mrec;
	/* Sanity checks are performed elsewhere. */
	ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
	ctx->is_first = TRUE;
	ctx->ntfs_ino = ni;
	ctx->al_entry = NULL;
	ctx->base_ntfs_ino = NULL;
	ctx->base_mrec = NULL;
	ctx->base_attr = NULL;
}

/**
 * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context
 * @ctx:	attribute search context to reinitialize
 *
 * Reinitialize the attribute search context @ctx.
 *
 * This is used when a search for a new attribute is being started to reset
 * the search context to the beginning.
 */
void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx)
{
	if (!ctx->base_ntfs_ino) {
		/* No attribute list. */
		ctx->is_first = TRUE;
		/* Sanity checks are performed elsewhere. */
		ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
				le16_to_cpu(ctx->mrec->attrs_offset));
		/*
		 * This needs resetting due to ntfs_external_attr_find() which
		 * can leave it set despite having zeroed ctx->base_ntfs_ino.
		 */
		ctx->al_entry = NULL;
		return;
	} /* Attribute list. */
	ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec);
	return;
}

/**
 * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context
 * @ni:		ntfs inode with which to initialize the search context
 * @mrec:	mft record with which to initialize the search context
 *
 * Allocate a new attribute search context, initialize it with @ni and @mrec,
 * and return it. Return NULL on error with errno set.
 *
 * @mrec can be NULL, in which case the mft record is taken from @ni.
 *
 * Note: For low level utilities which know what they are doing we allow @ni to
 * be NULL and @mrec to be set.  Do NOT do this unless you understand the
 * implications!!!  For example it is no longer safe to call ntfs_attr_lookup().
 */
ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec)
{
	ntfs_attr_search_ctx *ctx;

	if (!ni && !mrec) {
		errno = EINVAL;
		ntfs_log_perror("NULL arguments");
		return NULL;
	}
	ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx));
	if (ctx)
		ntfs_attr_init_search_ctx(ctx, ni, mrec);
	return ctx;
}

/**
 * ntfs_attr_put_search_ctx - release an attribute search context
 * @ctx:	attribute search context to free
 *
 * Release the attribute search context @ctx.
 */
void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
{
	// NOTE: save errno if it could change and function stays void!
	free(ctx);
}

/**
 * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
 * @vol:	ntfs volume to which the attribute belongs
 * @type:	attribute type which to find
 *
 * Search for the attribute definition record corresponding to the attribute
 * @type in the $AttrDef system file.
 *
 * Return the attribute type definition record if found and NULL if not found
 * or an error occurred. On error the error code is stored in errno. The
 * following error codes are defined:
 *	ENOENT	- The attribute @type is not specified in $AttrDef.
 *	EINVAL	- Invalid parameters (e.g. @vol is not valid).
 */
ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
		const ATTR_TYPES type)
{
	ATTR_DEF *ad;

	if (!vol || !vol->attrdef || !type) {
		errno = EINVAL;
		ntfs_log_perror("%s: type=%d", __FUNCTION__, type);
		return NULL;
	}
	for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
			vol->attrdef_len && ad->type; ++ad) {
		/* We haven't found it yet, carry on searching. */
		if (le32_to_cpu(ad->type) < le32_to_cpu(type))
			continue;
		/* We found the attribute; return it. */
		if (ad->type == type)
			return ad;
		/* We have gone too far already. No point in continuing. */
		break;
	}
	errno = ENOENT;
	ntfs_log_perror("%s: type=%d", __FUNCTION__, type);
	return NULL;
}

/**
 * ntfs_attr_size_bounds_check - check a size of an attribute type for validity
 * @vol:	ntfs volume to which the attribute belongs
 * @type:	attribute type which to check
 * @size:	size which to check
 *
 * Check whether the @size in bytes is valid for an attribute of @type on the
 * ntfs volume @vol. This information is obtained from $AttrDef system file.
 *
 * Return 0 if valid and -1 if not valid or an error occurred. On error the
 * error code is stored in errno. The following error codes are defined:
 *	ERANGE	- @size is not valid for the attribute @type.
 *	ENOENT	- The attribute @type is not specified in $AttrDef.
 *	EINVAL	- Invalid parameters (e.g. @size is < 0 or @vol is not valid).
 */
int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type,
		const s64 size)
{
	ATTR_DEF *ad;
	s64 min_size, max_size;

	if (size < 0) {
		errno = EINVAL;
		ntfs_log_perror("%s: size=%lld", __FUNCTION__,
				(long long)size);
		return -1;
	}

	/*
	 * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise 
	 * Windows would crash. This is not listed in the AttrDef.
	 */
	if (type == AT_ATTRIBUTE_LIST && size > 0x40000) {
		errno = ERANGE;
		ntfs_log_perror("Too large attrlist (%lld)", (long long)size);
		return -1;
	}

	ad = ntfs_attr_find_in_attrdef(vol, type);
	if (!ad)
		return -1;
	
	min_size = sle64_to_cpu(ad->min_size);
	max_size = sle64_to_cpu(ad->max_size);

	/* The $AttrDef generated by Windows specifies 2 as min_size for the
	 * volume name attribute, but in reality Windows sets it to 0 when
	 * clearing the volume name. If we want to be able to clear the volume
	 * name we must also accept 0 as min_size, despite the $AttrDef
	 * definition. */
	if(type == AT_VOLUME_NAME)
		min_size = 0;
	
	if ((min_size && (size < min_size)) || 
	    ((max_size > 0) && (size > max_size))) {
		errno = ERANGE;
		ntfs_log_perror("Attr type %d size check failed (min,size,max="
			"%lld,%lld,%lld)", type, (long long)min_size,
			(long long)size, (long long)max_size);
		return -1;
	}
	return 0;
}

/**
 * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident
 * @vol:	ntfs volume to which the attribute belongs
 * @type:	attribute type to check
 * @name:	attribute name to check
 * @name_len:	attribute name length
 *
 * Check whether the attribute of @type and @name with name length @name_len on
 * the ntfs volume @vol is allowed to be non-resident.  This information is
 * obtained from $AttrDef system file and is augmented by rules imposed by
 * Microsoft (e.g. see http://support.microsoft.com/kb/974729/).
 *
 * Return 0 if the attribute is allowed to be non-resident and -1 if not or an
 * error occurred. On error the error code is stored in errno. The following
 * error codes are defined:
 *	EPERM	- The attribute is not allowed to be non-resident.
 *	ENOENT	- The attribute @type is not specified in $AttrDef.
 *	EINVAL	- Invalid parameters (e.g. @vol is not valid).
 */
static int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type,
					const ntfschar *name, int name_len)
{
	ATTR_DEF *ad;
	BOOL allowed;

	/*
	 * Microsoft has decreed that $LOGGED_UTILITY_STREAM attributes with a
	 * name of $TXF_DATA must be resident despite the entry for
	 * $LOGGED_UTILITY_STREAM in $AttrDef allowing them to be non-resident.
	 * Failure to obey this on the root directory mft record of a volume
	 * causes Windows Vista and later to see the volume as a RAW volume and
	 * thus cannot mount it at all.
	 */
	if ((type == AT_LOGGED_UTILITY_STREAM)
	    && name
	    && ntfs_names_are_equal(TXF_DATA, 9, name, name_len,
			CASE_SENSITIVE, vol->upcase, vol->upcase_len))
		allowed = FALSE;
	else {
		/* Find the attribute definition record in $AttrDef. */
		ad = ntfs_attr_find_in_attrdef(vol, type);
		if (!ad)
			return -1;
		/* Check the flags and return the result. */
		allowed = !(ad->flags & ATTR_DEF_RESIDENT);
	}
	if (!allowed) {
		errno = EPERM;
		ntfs_log_trace("Attribute can't be non-resident\n");
		return -1;
	}
	return 0;
}

/**
 * ntfs_attr_can_be_resident - check if an attribute can be resident
 * @vol:	ntfs volume to which the attribute belongs
 * @type:	attribute type which to check
 *
 * Check whether the attribute of @type on the ntfs volume @vol is allowed to
 * be resident. This information is derived from our ntfs knowledge and may
 * not be completely accurate, especially when user defined attributes are
 * present. Basically we allow everything to be resident except for index
 * allocation and extended attribute attributes.
 *
 * Return 0 if the attribute is allowed to be resident and -1 if not or an
 * error occurred. On error the error code is stored in errno. The following
 * error codes are defined:
 *	EPERM	- The attribute is not allowed to be resident.
 *	EINVAL	- Invalid parameters (e.g. @vol is not valid).
 *
 * Warning: In the system file $MFT the attribute $Bitmap must be non-resident
 *	    otherwise windows will not boot (blue screen of death)!  We cannot
 *	    check for this here as we don't know which inode's $Bitmap is being
 *	    asked about so the caller needs to special case this.
 */
int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type)
{
	if (!vol || !vol->attrdef || !type) {
		errno = EINVAL;
		return -1;
	}
	if (type != AT_INDEX_ALLOCATION)
		return 0;
	
	ntfs_log_trace("Attribute can't be resident\n");
	errno = EPERM;
	return -1;
}

/**
 * ntfs_make_room_for_attr - make room for an attribute inside an mft record
 * @m:		mft record
 * @pos:	position at which to make space
 * @size:	byte size to make available at this position
 *
 * @pos points to the attribute in front of which we want to make space.
 *
 * Return 0 on success or -1 on error. On error the error code is stored in
 * errno. Possible error codes are:
 *	ENOSPC	- There is not enough space available to complete operation. The
 *		  caller has to make space before calling this.
 *	EINVAL	- Input parameters were faulty.
 */
int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size)
{
	u32 biu;

	ntfs_log_trace("Entering for pos 0x%d, size %u.\n",
		(int)(pos - (u8*)m), (unsigned) size);

	/* Make size 8-byte alignment. */
	size = (size + 7) & ~7;

	/* Rigorous consistency checks. */
	if (!m || !pos || pos < (u8*)m) {
		errno = EINVAL;
		ntfs_log_perror("%s: pos=%p  m=%p", __FUNCTION__, pos, m);
		return -1;
	}
	/* The -8 is for the attribute terminator. */
	if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) {
		errno = EINVAL;
		return -1;
	}
	/* Nothing to do. */
	if (!size)
		return 0;

	biu = le32_to_cpu(m->bytes_in_use);
	/* Do we have enough space? */
	if (biu + size > le32_to_cpu(m->bytes_allocated) ||
	    pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) {
		errno = ENOSPC;
		ntfs_log_trace("No enough space in the MFT record\n");
		return -1;
	}
	/* Move everything after pos to pos + size. */
	memmove(pos + size, pos, biu - (pos - (u8*)m));
	/* Update mft record. */
	m->bytes_in_use = cpu_to_le32(biu + size);
	return 0;
}

/**
 * ntfs_resident_attr_record_add - add resident attribute to inode
 * @ni:		opened ntfs inode to which MFT record add attribute
 * @type:	type of the new attribute
 * @name:	name of the new attribute
 * @name_len:	name length of the new attribute
 * @val:	value of the new attribute
 * @size:	size of new attribute (length of @val, if @val != NULL)
 * @flags:	flags of the new attribute
 *
 * Return offset to attribute from the beginning of the mft record on success
 * and -1 on error. On error the error code is stored in errno.
 * Possible error codes are:
 *	EINVAL	- Invalid arguments passed to function.
 *	EEXIST	- Attribute of such type and with same name already exists.
 *	EIO	- I/O error occurred or damaged filesystem.
 */
int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
			const ntfschar *name, u8 name_len, const u8 *val,
			u32 size, ATTR_FLAGS data_flags)
{
	ntfs_attr_search_ctx *ctx;
	u32 length;
	ATTR_RECORD *a;
	MFT_RECORD *m;
	int err, offset;
	ntfs_inode *base_ni;

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n",
		(long long) ni->mft_no, (unsigned) type, (unsigned) data_flags);

	if (!ni || (!name && name_len)) {
		errno = EINVAL;
		return -1;
	}

	if (ntfs_attr_can_be_resident(ni->vol, type)) {
		if (errno == EPERM)
			ntfs_log_trace("Attribute can't be resident.\n");
		else
			ntfs_log_trace("ntfs_attr_can_be_resident failed.\n");
		return -1;
	}

	/* Locate place where record should be. */
	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx)
		return -1;
	/*
	 * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
	 * attribute in @ni->mrec, not any extent inode in case if @ni is base
	 * file record.
	 */
	if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size,
			ctx)) {
		err = EEXIST;
		ntfs_log_trace("Attribute already present.\n");
		goto put_err_out;
	}
	if (errno != ENOENT) {
		err = EIO;
		goto put_err_out;
	}
	a = ctx->attr;
	m = ctx->mrec;

	/* Make room for attribute. */
	length = offsetof(ATTR_RECORD, resident_end) +
				((name_len * sizeof(ntfschar) + 7) & ~7) +
				((size + 7) & ~7);
	if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) {
		err = errno;
		ntfs_log_trace("Failed to make room for attribute.\n");
		goto put_err_out;
	}

	/* Setup record fields. */
	offset = ((u8*)a - (u8*)m);
	a->type = type;
	a->length = cpu_to_le32(length);
	a->non_resident = 0;
	a->name_length = name_len;
	a->name_offset = (name_len
		? cpu_to_le16(offsetof(ATTR_RECORD, resident_end))
		: const_cpu_to_le16(0));
	a->flags = data_flags;
	a->instance = m->next_attr_instance;
	a->value_length = cpu_to_le32(size);
	a->value_offset = cpu_to_le16(length - ((size + 7) & ~7));
	if (val)
		memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size);
	else
		memset((u8*)a + le16_to_cpu(a->value_offset), 0, size);
	if (type == AT_FILE_NAME)
		a->resident_flags = RESIDENT_ATTR_IS_INDEXED;
	else
		a->resident_flags = 0;
	if (name_len)
		memcpy((u8*)a + le16_to_cpu(a->name_offset),
			name, sizeof(ntfschar) * name_len);
	m->next_attr_instance =
		cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff);
	if (ni->nr_extents == -1)
		base_ni = ni->base_ni;
	else
		base_ni = ni;
	if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) {
		if (ntfs_attrlist_entry_add(ni, a)) {
			err = errno;
			ntfs_attr_record_resize(m, a, 0);
			ntfs_log_trace("Failed add attribute entry to "
					"ATTRIBUTE_LIST.\n");
			goto put_err_out;
		}
	}
	if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
	    ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30
	    : type == AT_DATA && name == AT_UNNAMED) {
		ni->data_size = size;
		ni->allocated_size = (size + 7) & ~7;
		set_nino_flag(ni,KnownSize);
	}
	ntfs_inode_mark_dirty(ni);
	ntfs_attr_put_search_ctx(ctx);
	return offset;
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
	errno = err;
	return -1;
}

/**
 * ntfs_non_resident_attr_record_add - add extent of non-resident attribute
 * @ni:			opened ntfs inode to which MFT record add attribute
 * @type:		type of the new attribute extent
 * @name:		name of the new attribute extent
 * @name_len:		name length of the new attribute extent
 * @lowest_vcn:		lowest vcn of the new attribute extent
 * @dataruns_size:	dataruns size of the new attribute extent
 * @flags:		flags of the new attribute extent
 *
 * Return offset to attribute from the beginning of the mft record on success
 * and -1 on error. On error the error code is stored in errno.
 * Possible error codes are:
 *	EINVAL	- Invalid arguments passed to function.
 *	EEXIST	- Attribute of such type, with same lowest vcn and with same
 *		  name already exists.
 *	EIO	- I/O error occurred or damaged filesystem.
 */
int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
		const ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
		ATTR_FLAGS flags)
{
	ntfs_attr_search_ctx *ctx;
	u32 length;
	ATTR_RECORD *a;
	MFT_RECORD *m;
	ntfs_inode *base_ni;
	int err, offset;

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, "
			"dataruns_size %d, flags 0x%x.\n",
			(long long) ni->mft_no, (unsigned) type,
			(long long) lowest_vcn, dataruns_size, (unsigned) flags);

	if (!ni || dataruns_size <= 0 || (!name && name_len)) {
		errno = EINVAL;
		return -1;
	}

	if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) {
		if (errno == EPERM)
			ntfs_log_perror("Attribute can't be non resident");
		else
			ntfs_log_perror("ntfs_attr_can_be_non_resident failed");
		return -1;
	}

	/* Locate place where record should be. */
	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx)
		return -1;
	/*
	 * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
	 * attribute in @ni->mrec, not any extent inode in case if @ni is base
	 * file record.
	 */
	if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0,
			ctx)) {
		err = EEXIST;
		ntfs_log_perror("Attribute 0x%x already present", type);
		goto put_err_out;
	}
	if (errno != ENOENT) {
		ntfs_log_perror("ntfs_attr_find failed");
		err = EIO;
		goto put_err_out;
	}
	a = ctx->attr;
	m = ctx->mrec;

	/* Make room for attribute. */
	dataruns_size = (dataruns_size + 7) & ~7;
	length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) *
			name_len + 7) & ~7) + dataruns_size +
			((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ?
			sizeof(a->compressed_size) : 0);
	if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) {
		err = errno;
		ntfs_log_perror("Failed to make room for attribute");
		goto put_err_out;
	}

	/* Setup record fields. */
	a->type = type;
	a->length = cpu_to_le32(length);
	a->non_resident = 1;
	a->name_length = name_len;
	a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) +
			((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ?
			sizeof(a->compressed_size) : 0));
	a->flags = flags;
	a->instance = m->next_attr_instance;
	a->lowest_vcn = cpu_to_sle64(lowest_vcn);
	a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size);
	a->compression_unit = (flags & ATTR_IS_COMPRESSED)
			? STANDARD_COMPRESSION_UNIT : 0;
	/* If @lowest_vcn == 0, than setup empty attribute. */
	if (!lowest_vcn) {
		a->highest_vcn = cpu_to_sle64(-1);
		a->allocated_size = 0;
		a->data_size = 0;
		a->initialized_size = 0;
		/* Set empty mapping pairs. */
		*((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0;
	}
	if (name_len)
		memcpy((u8*)a + le16_to_cpu(a->name_offset),
			name, sizeof(ntfschar) * name_len);
	m->next_attr_instance =
		cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff);
	if (ni->nr_extents == -1)
		base_ni = ni->base_ni;
	else
		base_ni = ni;
	if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) {
		if (ntfs_attrlist_entry_add(ni, a)) {
			err = errno;
			ntfs_log_perror("Failed add attr entry to attrlist");
			ntfs_attr_record_resize(m, a, 0);
			goto put_err_out;
		}
	}
	ntfs_inode_mark_dirty(ni);
	/*
	 * Locate offset from start of the MFT record where new attribute is
	 * placed. We need relookup it, because record maybe moved during
	 * update of attribute list.
	 */
	ntfs_attr_reinit_search_ctx(ctx);
	if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE,
					lowest_vcn, NULL, 0, ctx)) {
		ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__);
		ntfs_attr_put_search_ctx(ctx);
		return -1;

	}
	offset = (u8*)ctx->attr - (u8*)ctx->mrec;
	ntfs_attr_put_search_ctx(ctx);
	return offset;
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
	errno = err;
	return -1;
}

/**
 * ntfs_attr_record_rm - remove attribute extent
 * @ctx:	search context describing the attribute which should be removed
 *
 * If this function succeed, user should reinit search context if he/she wants
 * use it anymore.
 *
 * Return 0 on success and -1 on error. On error the error code is stored in
 * errno. Possible error codes are:
 *	EINVAL	- Invalid arguments passed to function.
 *	EIO	- I/O error occurred or damaged filesystem.
 */
int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx)
{
	ntfs_inode *base_ni, *ni;
	ATTR_TYPES type;

	if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) {
		errno = EINVAL;
		return -1;
	}

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
			(long long) ctx->ntfs_ino->mft_no,
			(unsigned) le32_to_cpu(ctx->attr->type));
	type = ctx->attr->type;
	ni = ctx->ntfs_ino;
	if (ctx->base_ntfs_ino)
		base_ni = ctx->base_ntfs_ino;
	else
		base_ni = ctx->ntfs_ino;

	/* Remove attribute itself. */
	if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) {
		ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT "
				"record.\n");
		if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST)
			if (ntfs_attrlist_entry_add(ni, ctx->attr))
				ntfs_log_trace("Rollback failed. Leaving inconstant "
						"metadata.\n");
		errno = EIO;
		return -1;
	}
	ntfs_inode_mark_dirty(ni);

	/*
	 * Remove record from $ATTRIBUTE_LIST if present and we don't want
	 * delete $ATTRIBUTE_LIST itself.
	 */
	if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) {
		if (ntfs_attrlist_entry_rm(ctx)) {
			ntfs_log_trace("Couldn't delete record from "
					"$ATTRIBUTE_LIST.\n");
			return -1;
		}
	}

	/* Post $ATTRIBUTE_LIST delete setup. */
	if (type == AT_ATTRIBUTE_LIST) {
		if (NInoAttrList(base_ni) && base_ni->attr_list)
			free(base_ni->attr_list);
		base_ni->attr_list = NULL;
		NInoClearAttrList(base_ni);
		NInoAttrListClearDirty(base_ni);
	}

	/* Free MFT record, if it doesn't contain attributes. */
	if (le32_to_cpu(ctx->mrec->bytes_in_use) -
			le16_to_cpu(ctx->mrec->attrs_offset) == 8) {
		if (ntfs_mft_record_free(ni->vol, ni)) {
			// FIXME: We need rollback here.
			ntfs_log_trace("Couldn't free MFT record.\n");
			errno = EIO;
			return -1;
		}
		/* Remove done if we freed base inode. */
		if (ni == base_ni)
			return 0;
	}

	if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni))
		return 0;

	/* Remove attribute list if we don't need it any more. */
	if (!ntfs_attrlist_need(base_ni)) {
		ntfs_attr_reinit_search_ctx(ctx);
		if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE,
				0, NULL, 0, ctx)) {
			/*
			 * FIXME: Should we succeed here? Definitely something
			 * goes wrong because NInoAttrList(base_ni) returned
			 * that we have got attribute list.
			 */
			ntfs_log_trace("Couldn't find attribute list. Succeed "
					"anyway.\n");
			return 0;
		}
		/* Deallocate clusters. */
		if (ctx->attr->non_resident) {
			runlist *al_rl;

			al_rl = ntfs_mapping_pairs_decompress(base_ni->vol,
					ctx->attr, NULL);
			if (!al_rl) {
				ntfs_log_trace("Couldn't decompress attribute list "
						"runlist. Succeed anyway.\n");
				return 0;
			}
			if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) {
				ntfs_log_trace("Leaking clusters! Run chkdsk. "
						"Couldn't free clusters from "
						"attribute list runlist.\n");
			}
			free(al_rl);
		}
		/* Remove attribute record itself. */
		if (ntfs_attr_record_rm(ctx)) {
			/*
			 * FIXME: Should we succeed here? BTW, chkdsk doesn't
			 * complain if it find MFT record with attribute list,
			 * but without extents.
			 */
			ntfs_log_trace("Couldn't remove attribute list. Succeed "
					"anyway.\n");
			return 0;
		}
	}
	return 0;
}

/**
 * ntfs_attr_add - add attribute to inode
 * @ni:		opened ntfs inode to which add attribute
 * @type:	type of the new attribute
 * @name:	name in unicode of the new attribute
 * @name_len:	name length in unicode characters of the new attribute
 * @val:	value of new attribute
 * @size:	size of the new attribute / length of @val (if specified)
 *
 * @val should always be specified for always resident attributes (eg. FILE_NAME
 * attribute), for attributes that can become non-resident @val can be NULL
 * (eg. DATA attribute). @size can be specified even if @val is NULL, in this
 * case data size will be equal to @size and initialized size will be equal
 * to 0.
 *
 * If inode haven't got enough space to add attribute, add attribute to one of
 * it extents, if no extents present or no one of them have enough space, than
 * allocate new extent and add attribute to it.
 *
 * If on one of this steps attribute list is needed but not present, than it is
 * added transparently to caller. So, this function should not be called with
 * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call
 * ntfs_inode_add_attrlist instead.
 *
 * On success return 0. On error return -1 with errno set to the error code.
 */
int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
		ntfschar *name, u8 name_len, const u8 *val, s64 size)
{
	u32 attr_rec_size;
	int err, i, offset;
	BOOL is_resident;
	BOOL can_be_non_resident = FALSE;
	ntfs_inode *attr_ni;
	ntfs_attr *na;
	ATTR_FLAGS data_flags;

	if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) {
		errno = EINVAL;
		ntfs_log_perror("%s: ni=%p  size=%lld", __FUNCTION__, ni,
				(long long)size);
		return -1;
	}

	ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n",
			(long long)ni->mft_no, type, (long long)size);

	if (ni->nr_extents == -1)
		ni = ni->base_ni;

	/* Check the attribute type and the size. */
	if (ntfs_attr_size_bounds_check(ni->vol, type, size)) {
		if (errno == ENOENT)
			errno = EIO;
		return -1;
	}

	/* Sanity checks for always resident attributes. */
	if (ntfs_attr_can_be_non_resident(ni->vol, type, name, name_len)) {
		if (errno != EPERM) {
			err = errno;
			ntfs_log_perror("ntfs_attr_can_be_non_resident failed");
			goto err_out;
		}
		/* @val is mandatory. */
		if (!val) {
			errno = EINVAL;
			ntfs_log_perror("val is mandatory for always resident "
					"attributes");
			return -1;
		}
		if (size > ni->vol->mft_record_size) {
			errno = ERANGE;
			ntfs_log_perror("Attribute is too big");
			return -1;
		}
	} else
		can_be_non_resident = TRUE;

	/*
	 * Determine resident or not will be new attribute. We add 8 to size in
	 * non resident case for mapping pairs.
	 */
	if (!ntfs_attr_can_be_resident(ni->vol, type)) {
		is_resident = TRUE;
	} else {
		if (errno != EPERM) {
			err = errno;
			ntfs_log_perror("ntfs_attr_can_be_resident failed");
			goto err_out;
		}
		is_resident = FALSE;
	}
	/* Calculate attribute record size. */
	if (is_resident)
		attr_rec_size = offsetof(ATTR_RECORD, resident_end) +
				((name_len * sizeof(ntfschar) + 7) & ~7) +
				((size + 7) & ~7);
	else
		attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) +
				((name_len * sizeof(ntfschar) + 7) & ~7) + 8;

	/*
	 * If we have enough free space for the new attribute in the base MFT
	 * record, then add attribute to it.
	 */
	if (le32_to_cpu(ni->mrec->bytes_allocated) -
			le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) {
		attr_ni = ni;
		goto add_attr_record;
	}

	/* Try to add to extent inodes. */
	if (ntfs_inode_attach_all_extents(ni)) {
		err = errno;
		ntfs_log_perror("Failed to attach all extents to inode");
		goto err_out;
	}
	for (i = 0; i < ni->nr_extents; i++) {
		attr_ni = ni->extent_nis[i];
		if (le32_to_cpu(attr_ni->mrec->bytes_allocated) -
				le32_to_cpu(attr_ni->mrec->bytes_in_use) >=
				attr_rec_size)
			goto add_attr_record;
	}

	/* There is no extent that contain enough space for new attribute. */
	if (!NInoAttrList(ni)) {
		/* Add attribute list not present, add it and retry. */
		if (ntfs_inode_add_attrlist(ni)) {
			err = errno;
			ntfs_log_perror("Failed to add attribute list");
			goto err_out;
		}
		return ntfs_attr_add(ni, type, name, name_len, val, size);
	}
	/* Allocate new extent. */
	attr_ni = ntfs_mft_record_alloc(ni->vol, ni);
	if (!attr_ni) {
		err = errno;
		ntfs_log_perror("Failed to allocate extent record");
		goto err_out;
	}

add_attr_record:
	if ((ni->flags & FILE_ATTR_COMPRESSED)
	    && (ni->vol->major_ver >= 3)
	    && NVolCompression(ni->vol)
	    && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)
	    && ((type == AT_DATA)
	       || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30))))
		data_flags = ATTR_IS_COMPRESSED;
	else
		data_flags = const_cpu_to_le16(0);
	if (is_resident) {
		/* Add resident attribute. */
		offset = ntfs_resident_attr_record_add(attr_ni, type, name,
				name_len, val, size, data_flags);
		if (offset < 0) {
			if (errno == ENOSPC && can_be_non_resident)
				goto add_non_resident;
			err = errno;
			ntfs_log_perror("Failed to add resident attribute");
			goto free_err_out;
		}
		return 0;
	}

add_non_resident:
	/* Add non resident attribute. */
	offset = ntfs_non_resident_attr_record_add(attr_ni, type, name,
				name_len, 0, 8, data_flags);
	if (offset < 0) {
		err = errno;
		ntfs_log_perror("Failed to add non resident attribute");
		goto free_err_out;
	}

	/* If @size == 0, we are done. */
	if (!size)
		return 0;

	/* Open new attribute and resize it. */
	na = ntfs_attr_open(ni, type, name, name_len);
	if (!na) {
		err = errno;
		ntfs_log_perror("Failed to open just added attribute");
		goto rm_attr_err_out;
	}
	/* Resize and set attribute value. */
	if (ntfs_attr_truncate_i(na, size, HOLES_OK) ||
			(val && (ntfs_attr_pwrite(na, 0, size, val) != size))) {
		err = errno;
		ntfs_log_perror("Failed to initialize just added attribute");
		if (ntfs_attr_rm(na))
			ntfs_log_perror("Failed to remove just added attribute");
		ntfs_attr_close(na);
		goto err_out;
	}
	ntfs_attr_close(na);
	return 0;

rm_attr_err_out:
	/* Remove just added attribute. */
	if (ntfs_attr_record_resize(attr_ni->mrec,
			(ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0))
		ntfs_log_perror("Failed to remove just added attribute #2");
free_err_out:
	/* Free MFT record, if it doesn't contain attributes. */
	if (le32_to_cpu(attr_ni->mrec->bytes_in_use) -
			le16_to_cpu(attr_ni->mrec->attrs_offset) == 8)
		if (ntfs_mft_record_free(attr_ni->vol, attr_ni))
			ntfs_log_perror("Failed to free MFT record");
err_out:
	errno = err;
	return -1;
}

/*
 *		Change an attribute flag
 */

int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, const ntfschar *name,
		u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask)
{
	ntfs_attr_search_ctx *ctx;
	int res;

	res = -1;
	/* Search for designated attribute */
	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (ctx) {
		if (!ntfs_attr_lookup(type, name, name_len,
					CASE_SENSITIVE, 0, NULL, 0, ctx)) {
			/* do the requested change (all small endian le16) */
			ctx->attr->flags = (ctx->attr->flags & ~mask)
						| (flags & mask);
			NInoSetDirty(ni);
			res = 0;
		}
		ntfs_attr_put_search_ctx(ctx);
	}
	return (res);
}


/**
 * ntfs_attr_rm - remove attribute from ntfs inode
 * @na:		opened ntfs attribute to delete
 *
 * Remove attribute and all it's extents from ntfs inode. If attribute was non
 * resident also free all clusters allocated by attribute.
 *
 * Return 0 on success or -1 on error with errno set to the error code.
 */
int ntfs_attr_rm(ntfs_attr *na)
{
	ntfs_attr_search_ctx *ctx;
	int ret = 0;

	if (!na) {
		ntfs_log_trace("Invalid arguments passed.\n");
		errno = EINVAL;
		return -1;
	}

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
		(long long) na->ni->mft_no, na->type);

	/* Free cluster allocation. */
	if (NAttrNonResident(na)) {
		if (ntfs_attr_map_whole_runlist(na))
			return -1;
		if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) {
			ntfs_log_trace("Failed to free cluster allocation. Leaving "
					"inconstant metadata.\n");
			ret = -1;
		}
	}

	/* Search for attribute extents and remove them all. */
	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx)
		return -1;
	while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
				CASE_SENSITIVE, 0, NULL, 0, ctx)) {
		if (ntfs_attr_record_rm(ctx)) {
			ntfs_log_trace("Failed to remove attribute extent. Leaving "
					"inconstant metadata.\n");
			ret = -1;
		}
		ntfs_attr_reinit_search_ctx(ctx);
	}
	ntfs_attr_put_search_ctx(ctx);
	if (errno != ENOENT) {
		ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant "
				"metadata.\n");
		ret = -1;
	}

	return ret;
}

/**
 * ntfs_attr_record_resize - resize an attribute record
 * @m:		mft record containing attribute record
 * @a:		attribute record to resize
 * @new_size:	new size in bytes to which to resize the attribute record @a
 *
 * Resize the attribute record @a, i.e. the resident part of the attribute, in
 * the mft record @m to @new_size bytes.
 *
 * Return 0 on success and -1 on error with errno set to the error code.
 * The following error codes are defined:
 *	ENOSPC	- Not enough space in the mft record @m to perform the resize.
 * Note that on error no modifications have been performed whatsoever.
 *
 * Warning: If you make a record smaller without having copied all the data you
 *	    are interested in the data may be overwritten!
 */
int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
{
	u32 old_size, alloc_size, attr_size;
	
	old_size   = le32_to_cpu(m->bytes_in_use);
	alloc_size = le32_to_cpu(m->bytes_allocated);
	attr_size  = le32_to_cpu(a->length);
	
	ntfs_log_trace("Sizes: old=%u alloc=%u attr=%u new=%u\n", 
		       (unsigned)old_size, (unsigned)alloc_size, 
		       (unsigned)attr_size, (unsigned)new_size);

	/* Align to 8 bytes, just in case the caller hasn't. */
	new_size = (new_size + 7) & ~7;
	
	/* If the actual attribute length has changed, move things around. */
	if (new_size != attr_size) {
		
		u32 new_muse = old_size - attr_size + new_size;
		
		/* Not enough space in this mft record. */
		if (new_muse > alloc_size) {
			errno = ENOSPC;
			ntfs_log_trace("Not enough space in the MFT record "
				       "(%u > %u)\n", new_muse, alloc_size);
			return -1;
		}

		if (a->type == AT_INDEX_ROOT && new_size > attr_size &&
		    new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) {
			errno = ENOSPC;
			ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n",
					new_muse, alloc_size);
			return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT;
		}
		
		/* Move attributes following @a to their new location. */
		memmove((u8 *)a + new_size, (u8 *)a + attr_size,
			old_size - ((u8 *)a - (u8 *)m) - attr_size);
		
		/* Adjust @m to reflect the change in used space. */
		m->bytes_in_use = cpu_to_le32(new_muse);
		
		/* Adjust @a to reflect the new size. */
		if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length))
			a->length = cpu_to_le32(new_size);
	}
	return 0;
}

/**
 * ntfs_resident_attr_value_resize - resize the value of a resident attribute
 * @m:		mft record containing attribute record
 * @a:		attribute record whose value to resize
 * @new_size:	new size in bytes to which to resize the attribute value of @a
 *
 * Resize the value of the attribute @a in the mft record @m to @new_size bytes.
 * If the value is made bigger, the newly "allocated" space is cleared.
 *
 * Return 0 on success and -1 on error with errno set to the error code.
 * The following error codes are defined:
 *	ENOSPC	- Not enough space in the mft record @m to perform the resize.
 * Note that on error no modifications have been performed whatsoever.
 */
int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
		const u32 new_size)
{
	int ret;
	
	ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size);

	/* Resize the resident part of the attribute record. */
	if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) +
			new_size + 7) & ~7)) < 0)
		return ret;
	/*
	 * If we made the attribute value bigger, clear the area between the
	 * old size and @new_size.
	 */
	if (new_size > le32_to_cpu(a->value_length))
		memset((u8*)a + le16_to_cpu(a->value_offset) +
				le32_to_cpu(a->value_length), 0, new_size -
				le32_to_cpu(a->value_length));
	/* Finally update the length of the attribute value. */
	a->value_length = cpu_to_le32(new_size);
	return 0;
}

/**
 * ntfs_attr_record_move_to - move attribute record to target inode
 * @ctx:	attribute search context describing the attribute record
 * @ni:		opened ntfs inode to which move attribute record
 *
 * If this function succeed, user should reinit search context if he/she wants
 * use it anymore.
 *
 * Return 0 on success and -1 on error with errno set to the error code.
 */
int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni)
{
	ntfs_attr_search_ctx *nctx;
	ATTR_RECORD *a;
	int err;

	if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) {
		ntfs_log_trace("Invalid arguments passed.\n");
		errno = EINVAL;
		return -1;
	}

	ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no "
			"0x%llx, ni->mft_no 0x%llx.\n",
			(unsigned) le32_to_cpu(ctx->attr->type),
			(long long) ctx->ntfs_ino->mft_no,
			(long long) ni->mft_no);

	if (ctx->ntfs_ino == ni)
		return 0;

	if (!ctx->al_entry) {
		ntfs_log_trace("Inode should contain attribute list to use this "
				"function.\n");
		errno = EINVAL;
		return -1;
	}

	/* Find place in MFT record where attribute will be moved. */
	a = ctx->attr;
	nctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!nctx)
		return -1;

	/*
	 * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
	 * attribute in @ni->mrec, not any extent inode in case if @ni is base
	 * file record.
	 */
	if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu(
			a->name_offset)), a->name_length, CASE_SENSITIVE, NULL,
			0, nctx)) {
		ntfs_log_trace("Attribute of such type, with same name already "
				"present in this MFT record.\n");
		err = EEXIST;
		goto put_err_out;
	}
	if (errno != ENOENT) {
		err = errno;
		ntfs_log_debug("Attribute lookup failed.\n");
		goto put_err_out;
	}

	/* Make space and move attribute. */
	if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr,
					le32_to_cpu(a->length))) {
		err = errno;
		ntfs_log_trace("Couldn't make space for attribute.\n");
		goto put_err_out;
	}
	memcpy(nctx->attr, a, le32_to_cpu(a->length));
	nctx->attr->instance = nctx->mrec->next_attr_instance;
	nctx->mrec->next_attr_instance = cpu_to_le16(
		(le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff);
	ntfs_attr_record_resize(ctx->mrec, a, 0);
	ntfs_inode_mark_dirty(ctx->ntfs_ino);
	ntfs_inode_mark_dirty(ni);

	/* Update attribute list. */
	ctx->al_entry->mft_reference =
		MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
	ctx->al_entry->instance = nctx->attr->instance;
	ntfs_attrlist_mark_dirty(ni);

	ntfs_attr_put_search_ctx(nctx);
	return 0;
put_err_out:
	ntfs_attr_put_search_ctx(nctx);
	errno = err;
	return -1;
}

/**
 * ntfs_attr_record_move_away - move away attribute record from it's mft record
 * @ctx:	attribute search context describing the attribute record
 * @extra:	minimum amount of free space in the new holder of record
 *
 * New attribute record holder must have free @extra bytes after moving
 * attribute record to it.
 *
 * If this function succeed, user should reinit search context if he/she wants
 * use it anymore.
 *
 * Return 0 on success and -1 on error with errno set to the error code.
 */
int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra)
{
	ntfs_inode *base_ni, *ni;
	MFT_RECORD *m;
	int i;

	if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) {
		errno = EINVAL;
		ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__,
				ctx, ctx ? ctx->attr : NULL, extra);
		return -1;
	}

	ntfs_log_trace("Entering for attr 0x%x, inode %llu\n",
			(unsigned) le32_to_cpu(ctx->attr->type),
			(unsigned long long)ctx->ntfs_ino->mft_no);

	if (ctx->ntfs_ino->nr_extents == -1)
		base_ni = ctx->base_ntfs_ino;
	else
		base_ni = ctx->ntfs_ino;

	if (!NInoAttrList(base_ni)) {
		errno = EINVAL;
		ntfs_log_perror("Inode %llu has no attrlist", 
				(unsigned long long)base_ni->mft_no);
		return -1;
	}

	if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) {
		ntfs_log_perror("Couldn't attach extents, inode=%llu", 
				(unsigned long long)base_ni->mft_no);
		return -1;
	}

	/* Walk through all extents and try to move attribute to them. */
	for (i = 0; i < base_ni->nr_extents; i++) {
		ni = base_ni->extent_nis[i];
		m = ni->mrec;

		if (ctx->ntfs_ino->mft_no == ni->mft_no)
			continue;

		if (le32_to_cpu(m->bytes_allocated) -
				le32_to_cpu(m->bytes_in_use) <
				le32_to_cpu(ctx->attr->length) + extra)
			continue;

		/*
		 * ntfs_attr_record_move_to can fail if extent with other lowest
		 * VCN already present in inode we trying move record to. So,
		 * do not return error.
		 */
		if (!ntfs_attr_record_move_to(ctx, ni))
			return 0;
	}

	/*
	 * Failed to move attribute to one of the current extents, so allocate
	 * new extent and move attribute to it.
	 */
	ni = ntfs_mft_record_alloc(base_ni->vol, base_ni);
	if (!ni) {
		ntfs_log_perror("Couldn't allocate MFT record");
		return -1;
	}
	if (ntfs_attr_record_move_to(ctx, ni)) {
		ntfs_log_perror("Couldn't move attribute to MFT record");
		return -1;
	}
	return 0;
}

/**
 * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
 * @na:		open ntfs attribute to make non-resident
 * @ctx:	ntfs search context describing the attribute
 *
 * Convert a resident ntfs attribute to a non-resident one.
 *
 * Return 0 on success and -1 on error with errno set to the error code. The
 * following error codes are defined:
 *	EPERM	- The attribute is not allowed to be non-resident.
 *	TODO: others...
 *
 * NOTE to self: No changes in the attribute list are required to move from
 *		 a resident to a non-resident attribute.
 *
 * Warning: We do not set the inode dirty and we do not write out anything!
 *	    We expect the caller to do this as this is a fairly low level
 *	    function and it is likely there will be further changes made.
 */
int ntfs_attr_make_non_resident(ntfs_attr *na,
		ntfs_attr_search_ctx *ctx)
{
	s64 new_allocated_size, bw;
	ntfs_volume *vol = na->ni->vol;
	ATTR_REC *a = ctx->attr;
	runlist *rl;
	int mp_size, mp_ofs, name_ofs, arec_size, err;

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
			long)na->ni->mft_no, na->type);

	/* Some preliminary sanity checking. */
	if (NAttrNonResident(na)) {
		ntfs_log_trace("Eeek!  Trying to make non-resident attribute "
				"non-resident.  Aborting...\n");
		errno = EINVAL;
		return -1;
	}

	/* Check that the attribute is allowed to be non-resident. */
	if (ntfs_attr_can_be_non_resident(vol, na->type, na->name, na->name_len))
		return -1;

	new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size
			- 1) & ~(vol->cluster_size - 1);

	if (new_allocated_size > 0) {
			if ((a->flags & ATTR_COMPRESSION_MASK)
					== ATTR_IS_COMPRESSED) {
				/* must allocate full compression blocks */
				new_allocated_size = ((new_allocated_size - 1)
					| ((1L << (STANDARD_COMPRESSION_UNIT
					   + vol->cluster_size_bits)) - 1)) + 1;
			}
		/* Start by allocating clusters to hold the attribute value. */
		rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >>
				vol->cluster_size_bits, -1, DATA_ZONE);
		if (!rl)
			return -1;
	} else
		rl = NULL;
	/*
	 * Setup the in-memory attribute structure to be non-resident so that
	 * we can use ntfs_attr_pwrite().
	 */
	NAttrSetNonResident(na);
	NAttrSetBeingNonResident(na);
	na->rl = rl;
	na->allocated_size = new_allocated_size;
	na->data_size = na->initialized_size = le32_to_cpu(a->value_length);
	/*
	 * FIXME: For now just clear all of these as we don't support them when
	 * writing.
	 */
	NAttrClearSparse(na);
	NAttrClearEncrypted(na);
	if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) {
			/* set compression writing parameters */
		na->compression_block_size
			= 1 << (STANDARD_COMPRESSION_UNIT + vol->cluster_size_bits);
		na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT;
	}

	if (rl) {
		/* Now copy the attribute value to the allocated cluster(s). */
		bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length),
				(u8*)a + le16_to_cpu(a->value_offset));
		if (bw != le32_to_cpu(a->value_length)) {
			err = errno;
			ntfs_log_debug("Eeek!  Failed to write out attribute value "
					"(bw = %lli, errno = %i).  "
					"Aborting...\n", (long long)bw, err);
			if (bw >= 0)
				err = EIO;
			goto cluster_free_err_out;
		}
	}
	/* Determine the size of the mapping pairs array. */
	mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX);
	if (mp_size < 0) {
		err = errno;
		ntfs_log_debug("Eeek!  Failed to get size for mapping pairs array.  "
				"Aborting...\n");
		goto cluster_free_err_out;
	}
	/* Calculate new offsets for the name and the mapping pairs array. */
	if (na->ni->flags & FILE_ATTR_COMPRESSED)
		name_ofs = (sizeof(ATTR_REC) + 7) & ~7;
	else
		name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7;
	mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
	/*
	 * Determine the size of the resident part of the non-resident
	 * attribute record. (Not compressed thus no compressed_size element
	 * present.)
	 */
	arec_size = (mp_ofs + mp_size + 7) & ~7;

	/* Resize the resident part of the attribute record. */
	if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) {
		err = errno;
		goto cluster_free_err_out;
	}

	/*
	 * Convert the resident part of the attribute record to describe a
	 * non-resident attribute.
	 */
	a->non_resident = 1;

	/* Move the attribute name if it exists and update the offset. */
	if (a->name_length)
		memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
				a->name_length * sizeof(ntfschar));
	a->name_offset = cpu_to_le16(name_ofs);

	/* Setup the fields specific to non-resident attributes. */
	a->lowest_vcn = cpu_to_sle64(0);
	a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >>
						vol->cluster_size_bits);

	a->mapping_pairs_offset = cpu_to_le16(mp_ofs);

	/*
	 * Update the flags to match the in-memory ones.
	 * However cannot change the compression state if we had
	 * a fuse_file_info open with a mark for release.
	 * The decisions about compression can only be made when
	 * creating/recreating the stream, not when making non resident.
	 */
	a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED);
	if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) {
			/* support only ATTR_IS_COMPRESSED compression mode */
		a->compression_unit = STANDARD_COMPRESSION_UNIT;
		a->compressed_size = const_cpu_to_le64(0);
	} else {
		a->compression_unit = 0;
		a->flags &= ~ATTR_COMPRESSION_MASK;
		na->data_flags = a->flags;
	}

	memset(&a->reserved1, 0, sizeof(a->reserved1));

	a->allocated_size = cpu_to_sle64(new_allocated_size);
	a->data_size = a->initialized_size = cpu_to_sle64(na->data_size);

	/* Generate the mapping pairs array in the attribute record. */
	if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs,
			rl, 0, NULL) < 0) {
		// FIXME: Eeek! We need rollback! (AIA)
		ntfs_log_trace("Eeek!  Failed to build mapping pairs.  Leaving "
				"corrupt attribute record on disk.  In memory "
				"runlist is still intact!  Error code is %i.  "
				"FIXME:  Need to rollback instead!\n", errno);
		return -1;
	}

	/* Done! */
	return 0;

cluster_free_err_out:
	if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0)
		ntfs_log_trace("Eeek!  Failed to release allocated clusters in error "
				"code path.  Leaving inconsistent metadata...\n");
	NAttrClearNonResident(na);
	NAttrClearFullyMapped(na);
	na->allocated_size = na->data_size;
	na->rl = NULL;
	free(rl);
	errno = err;
	return -1;
}


static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize);

/**
 * ntfs_resident_attr_resize - resize a resident, open ntfs attribute
 * @na:		resident ntfs attribute to resize
 * @newsize:	new size (in bytes) to which to resize the attribute
 *
 * Change the size of a resident, open ntfs attribute @na to @newsize bytes.
 * Can also be used to force an attribute non-resident. In this case, the
 * size cannot be changed.
 *
 * On success return 0 
 * On error return values are:
 * 	STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT
 * 	STATUS_ERROR - otherwise
 * The following error codes are defined:
 *	ENOMEM - Not enough memory to complete operation.
 *	ERANGE - @newsize is not valid for the attribute type of @na.
 *	ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST.
 */
static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize,
			BOOL force_non_resident)
{
	ntfs_attr_search_ctx *ctx;
	ntfs_volume *vol;
	ntfs_inode *ni;
	int err, ret = STATUS_ERROR;

	ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", 
		       (unsigned long long)na->ni->mft_no, na->type, 
		       (long long)newsize);

	/* Get the attribute record that needs modification. */
	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx)
		return -1;
	if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0,
			ctx)) {
		err = errno;
		ntfs_log_perror("ntfs_attr_lookup failed");
		goto put_err_out;
	}
	vol = na->ni->vol;
	/*
	 * Check the attribute type and the corresponding minimum and maximum
	 * sizes against @newsize and fail if @newsize is out of bounds.
	 */
	if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
		err = errno;
		if (err == ENOENT)
			err = EIO;
		ntfs_log_perror("%s: bounds check failed", __FUNCTION__);
		goto put_err_out;
	}
	/*
	 * If @newsize is bigger than the mft record we need to make the
	 * attribute non-resident if the attribute type supports it. If it is
	 * smaller we can go ahead and attempt the resize.
	 */
	if ((newsize < vol->mft_record_size) && !force_non_resident) {
		/* Perform the resize of the attribute record. */
		if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
				newsize))) {
			/* Update attribute size everywhere. */
			na->data_size = na->initialized_size = newsize;
			na->allocated_size = (newsize + 7) & ~7;
			if ((na->data_flags & ATTR_COMPRESSION_MASK)
			    || NAttrSparse(na))
				na->compressed_size = na->allocated_size;
			if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
			    ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
			    : na->type == AT_DATA && na->name == AT_UNNAMED) {
				na->ni->data_size = na->data_size;
				if (((na->data_flags & ATTR_COMPRESSION_MASK)
					|| NAttrSparse(na))
						&& NAttrNonResident(na))
					na->ni->allocated_size
						= na->compressed_size;
				else
					na->ni->allocated_size
						= na->allocated_size;
				set_nino_flag(na->ni,KnownSize);
				if (na->type == AT_DATA)
					NInoFileNameSetDirty(na->ni);
			}
			goto resize_done;
		}
		/* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */
		if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) {
			err = errno;
			goto put_err_out;
		}
	}
	/* There is not enough space in the mft record to perform the resize. */

	/* Make the attribute non-resident if possible. */
	if (!ntfs_attr_make_non_resident(na, ctx)) {
		ntfs_inode_mark_dirty(ctx->ntfs_ino);
		ntfs_attr_put_search_ctx(ctx);
		/*
		 * do not truncate when forcing non-resident, this
		 * could cause the attribute to be made resident again,
		 * so size changes are not allowed.
		 */
		if (force_non_resident) {
			ret = 0;
			if (newsize != na->data_size) {
				ntfs_log_error("Cannot change size when"
					" forcing non-resident\n");
				errno = EIO;
				ret = STATUS_ERROR;
			}
			return (ret);
		}
		/* Resize non-resident attribute */
		return ntfs_attr_truncate_i(na, newsize, HOLES_OK);
	} else if (errno != ENOSPC && errno != EPERM) {
		err = errno;
		ntfs_log_perror("Failed to make attribute non-resident");
		goto put_err_out;
	}

	/* Try to make other attributes non-resident and retry each time. */
	ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec);
	while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {
		ntfs_attr *tna;
		ATTR_RECORD *a;

		a = ctx->attr;
		if (a->non_resident)
			continue;

		/*
		 * Check out whether convert is reasonable. Assume that mapping
		 * pairs will take 8 bytes.
		 */
		if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD,
				compressed_size) + ((a->name_length *
				sizeof(ntfschar) + 7) & ~7) + 8)
			continue;

		tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a +
				le16_to_cpu(a->name_offset)), a->name_length);
		if (!tna) {
			err = errno;
			ntfs_log_perror("Couldn't open attribute");
			goto put_err_out;
		}
		if (ntfs_attr_make_non_resident(tna, ctx)) {
			ntfs_attr_close(tna);
			continue;
		}
		if ((tna->type == AT_DATA) && !tna->name_len) {
			/*
			 * If we had to make the unnamed data attribute
			 * non-resident, propagate its new allocated size
			 * to all name attributes and directory indexes
			 */
			tna->ni->allocated_size = tna->allocated_size;
			NInoFileNameSetDirty(tna->ni);
		}
		if (((tna->data_flags & ATTR_COMPRESSION_MASK)
						== ATTR_IS_COMPRESSED)
		   && ntfs_attr_pclose(tna)) {
			err = errno;
			ntfs_attr_close(tna);
			goto put_err_out;
		}
		ntfs_inode_mark_dirty(tna->ni);
		ntfs_attr_close(tna);
		ntfs_attr_put_search_ctx(ctx);
		return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
	}
	/* Check whether error occurred. */
	if (errno != ENOENT) {
		err = errno;
		ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__);
		goto put_err_out;
	}
	
	/* 
	 * The standard information and attribute list attributes can't be
	 * moved out from the base MFT record, so try to move out others. 
	 */
	if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) {
		ntfs_attr_put_search_ctx(ctx);
		if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD,
				non_resident_end) + 8)) {
			ntfs_log_perror("Could not free space in MFT record");
			return -1;
		}
		return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
	}

	/*
	 * Move the attribute to a new mft record, creating an attribute list
	 * attribute or modifying it if it is already present.
	 */

	/* Point search context back to attribute which we need resize. */
	ntfs_attr_init_search_ctx(ctx, na->ni, NULL);
	if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
			0, NULL, 0, ctx)) {
		ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__);
		err = errno;
		goto put_err_out;
	}

	/*
	 * Check whether attribute is already single in this MFT record.
	 * 8 added for the attribute terminator.
	 */
	if (le32_to_cpu(ctx->mrec->bytes_in_use) ==
			le16_to_cpu(ctx->mrec->attrs_offset) +
			le32_to_cpu(ctx->attr->length) + 8) {
		err = ENOSPC;
		ntfs_log_trace("MFT record is filled with one attribute\n");
		ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT;
		goto put_err_out;
	}

	/* Add attribute list if not present. */
	if (na->ni->nr_extents == -1)
		ni = na->ni->base_ni;
	else
		ni = na->ni;
	if (!NInoAttrList(ni)) {
		ntfs_attr_put_search_ctx(ctx);
		if (ntfs_inode_add_attrlist(ni))
			return -1;
		return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);
	}
	/* Allocate new mft record. */
	ni = ntfs_mft_record_alloc(vol, ni);
	if (!ni) {
		err = errno;
		ntfs_log_perror("Couldn't allocate new MFT record");
		goto put_err_out;
	}
	/* Move attribute to it. */
	if (ntfs_attr_record_move_to(ctx, ni)) {
		err = errno;
		ntfs_log_perror("Couldn't move attribute to new MFT record");
		goto put_err_out;
	}
	/* Update ntfs attribute. */
	if (na->ni->nr_extents == -1)
		na->ni = ni;

	ntfs_attr_put_search_ctx(ctx);
	/* Try to perform resize once again. */
	return ntfs_resident_attr_resize_i(na, newsize, force_non_resident);

resize_done:
	/*
	 * Set the inode (and its base inode if it exists) dirty so it is
	 * written out later.
	 */
	ntfs_inode_mark_dirty(ctx->ntfs_ino);
	ntfs_attr_put_search_ctx(ctx);
	return 0;
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
	errno = err;
	return ret;
}

static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize)
{
	int ret; 
	
	ntfs_log_enter("Entering\n");
	ret = ntfs_resident_attr_resize_i(na, newsize, FALSE);
	ntfs_log_leave("\n");
	return ret;
}

/*
 *		Force an attribute to be made non-resident without
 *	changing its size.
 *
 *	This is particularly needed when the attribute has no data,
 *	as the non-resident variant requires more space in the MFT
 *	record, and may imply expelling some other attribute.
 *
 *	As a consequence the existing ntfs_attr_search_ctx's have to
 *	be closed or reinitialized.
 *
 *	returns 0 if successful,
 *		< 0 if failed, with errno telling why
 */

int ntfs_attr_force_non_resident(ntfs_attr *na)
{
	int res;

	res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE);
	if (!res && !NAttrNonResident(na)) {
		res = -1;
		errno = EIO;
		ntfs_log_error("Failed to force non-resident\n");
	}
	return (res);
}

/**
 * ntfs_attr_make_resident - convert a non-resident to a resident attribute
 * @na:		open ntfs attribute to make resident
 * @ctx:	ntfs search context describing the attribute
 *
 * Convert a non-resident ntfs attribute to a resident one.
 *
 * Return 0 on success and -1 on error with errno set to the error code. The
 * following error codes are defined:
 *	EINVAL	   - Invalid arguments passed.
 *	EPERM	   - The attribute is not allowed to be resident.
 *	EIO	   - I/O error, damaged inode or bug.
 *	ENOSPC	   - There is no enough space to perform conversion.
 *	EOPNOTSUPP - Requested conversion is not supported yet.
 *
 * Warning: We do not set the inode dirty and we do not write out anything!
 *	    We expect the caller to do this as this is a fairly low level
 *	    function and it is likely there will be further changes made.
 */
static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
{
	ntfs_volume *vol = na->ni->vol;
	ATTR_REC *a = ctx->attr;
	int name_ofs, val_ofs, err = EIO;
	s64 arec_size, bytes_read;

	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
			long)na->ni->mft_no, na->type);

	/* Should be called for the first extent of the attribute. */
	if (sle64_to_cpu(a->lowest_vcn)) {
		ntfs_log_trace("Eeek!  Should be called for the first extent of the "
				"attribute.  Aborting...\n");
		errno = EINVAL;
		return -1;
	}

	/* Some preliminary sanity checking. */
	if (!NAttrNonResident(na)) {
		ntfs_log_trace("Eeek!  Trying to make resident attribute resident.  "
				"Aborting...\n");
		errno = EINVAL;
		return -1;
	}

	/* Make sure this is not $MFT/$BITMAP or Windows will not boot! */
	if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) {
		errno = EPERM;
		return -1;
	}

	/* Check that the attribute is allowed to be resident. */
	if (ntfs_attr_can_be_resident(vol, na->type))
		return -1;

	if (na->data_flags & ATTR_IS_ENCRYPTED) {
		ntfs_log_trace("Making encrypted streams resident is not "
				"implemented yet.\n");
		errno = EOPNOTSUPP;
		return -1;
	}

	/* Work out offsets into and size of the resident attribute. */
	name_ofs = 24; /* = sizeof(resident_ATTR_REC); */
	val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
	arec_size = (val_ofs + na->data_size + 7) & ~7;

	/* Sanity check the size before we start modifying the attribute. */
	if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) +
			arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) {
		errno = ENOSPC;
		ntfs_log_trace("Not enough space to make attribute resident\n");
		return -1;
	}

	/* Read and cache the whole runlist if not already done. */
	if (ntfs_attr_map_whole_runlist(na))
		return -1;

	/* Move the attribute name if it exists and update the offset. */
	if (a->name_length) {
		memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
				a->name_length * sizeof(ntfschar));
	}
	a->name_offset = cpu_to_le16(name_ofs);

	/* Resize the resident part of the attribute record. */
	if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) {
		/*
		 * Bug, because ntfs_attr_record_resize should not fail (we
		 * already checked that attribute fits MFT record).
		 */
		ntfs_log_error("BUG! Failed to resize attribute record. "
				"Please report to the %s.  Aborting...\n",
				NTFS_DEV_LIST);
		errno = EIO;
		return -1;
	}

	/* Convert the attribute record to describe a resident attribute. */
	a->non_resident = 0;
	a->flags = 0;
	a->value_length = cpu_to_le32(na->data_size);
	a->value_offset = cpu_to_le16(val_ofs);
	/*
	 *  If a data stream was wiped out, adjust the compression mode
	 *  to current state of compression flag
	 */
	if (!na->data_size
	    && (na->type == AT_DATA)
	    && (na->ni->vol->major_ver >= 3)
	    && NVolCompression(na->ni->vol)
	    && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)
	    && (na->ni->flags & FILE_ATTR_COMPRESSED)) {
		a->flags |= ATTR_IS_COMPRESSED;
		na->data_flags = a->flags;
	}
	/*
	 * File names cannot be non-resident so we would never see this here
	 * but at least it serves as a reminder that there may be attributes
	 * for which we do need to set this flag. (AIA)
	 */
	if (a->type == AT_FILE_NAME)
		a->resident_flags = RESIDENT_ATTR_IS_INDEXED;
	else
		a->resident_flags = 0;
	a->reservedR = 0;

	/* Sanity fixup...  Shouldn't really happen. (AIA) */
	if (na->initialized_size > na->data_size)
		na->initialized_size = na->data_size;

	/* Copy data from run list to resident attribute value. */
	bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size,
			(u8*)a + val_ofs);
	if (bytes_read != na->initialized_size) {
		if (bytes_read < 0)
			err = errno;
		ntfs_log_trace("Eeek! Failed to read attribute data. Leaving "
				"inconstant metadata. Run chkdsk.  "
				"Aborting...\n");
		errno = err;
		return -1;
	}

	/* Clear memory in gap between initialized_size and data_size. */
	if (na->initialized_size < na->data_size)
		memset((u8*)a + val_ofs + na->initialized_size, 0,
				na->data_size - na->initialized_size);

	/*
	 * Deallocate clusters from the runlist.
	 *
	 * NOTE: We can use ntfs_cluster_free() because we have already mapped
	 * the whole run list and thus it doesn't matter that the attribute
	 * record is in a transiently corrupted state at this moment in time.
	 */
	if (ntfs_cluster_free(vol, na, 0, -1) < 0) {
		ntfs_log_perror("Eeek! Failed to release allocated clusters");
		ntfs_log_trace("Ignoring error and leaving behind wasted "
				"clusters.\n");
	}

	/* Throw away the now unused runlist. */
	free(na->rl);
	na->rl = NULL;

	/* Update in-memory struct ntfs_attr. */
	NAttrClearNonResident(na);
	NAttrClearFullyMapped(na);
	NAttrClearSparse(na);
	NAttrClearEncrypted(na);
	na->initialized_size = na->data_size;
	na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7;
	na->compression_block_size = 0;
	na->compression_block_size_bits = na->compression_block_clusters = 0;
	return 0;
}

/*
 * If we are in the first extent, then set/clean sparse bit,
 * update allocated and compressed size.
 */
static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
				hole_type holes, ntfs_attr_search_ctx *ctx)
{
	int sparse, ret = 0;
	
	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", 
		       (unsigned long long)na->ni->mft_no, na->type);
	
	if (a->lowest_vcn)
		goto out;

	a->allocated_size = cpu_to_sle64(na->allocated_size);

	/* Update sparse bit, unless this is an intermediate state */
	if (holes == HOLES_DELAY)
		sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0);
	else {
		sparse = ntfs_rl_sparse(na->rl);
		if (sparse == -1) {
			errno = EIO;
			goto error;
		}
	}

	/* Check whether attribute becomes sparse, unless check is delayed. */
	if ((holes != HOLES_DELAY)
	    && sparse
	    && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) {
		/*
		 * Move attribute to another mft record, if attribute is too 
		 * small to add compressed_size field to it and we have no 
		 * free space in the current mft record.
		 */
		if ((le32_to_cpu(a->length) - 
				le16_to_cpu(a->mapping_pairs_offset) == 8)
		    && !(le32_to_cpu(m->bytes_allocated) - 
				le32_to_cpu(m->bytes_in_use))) {

			if (!NInoAttrList(na->ni)) {
				ntfs_attr_put_search_ctx(ctx);
				if (ntfs_inode_add_attrlist(na->ni))
					goto leave;
				goto retry;
			}
			if (ntfs_attr_record_move_away(ctx, 8)) {
				ntfs_log_perror("Failed to move attribute");
				goto error;
			}
			ntfs_attr_put_search_ctx(ctx);
			goto retry;
		}
		if (!(le32_to_cpu(a->length) - le16_to_cpu(
						a->mapping_pairs_offset))) {
			errno = EIO;
			ntfs_log_perror("Mapping pairs space is 0");
			goto error;
		}
		
		NAttrSetSparse(na);
		a->flags |= ATTR_IS_SPARSE;
		na->data_flags = a->flags;
		a->compression_unit = STANDARD_COMPRESSION_UNIT;  /* Windows
		 set it so, even if attribute is not actually compressed. */
		
		memmove((u8*)a + le16_to_cpu(a->name_offset) + 8,
			(u8*)a + le16_to_cpu(a->name_offset),
			a->name_length * sizeof(ntfschar));

		a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8);
		
		a->mapping_pairs_offset =
			cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8);
	}

	/* Attribute no longer sparse. */
	if (!sparse && (a->flags & ATTR_IS_SPARSE) && 
	    !(a->flags & ATTR_IS_COMPRESSED)) {
		
		NAttrClearSparse(na);
		a->flags &= ~ATTR_IS_SPARSE;
		na->data_flags = a->flags;
		a->compression_unit = 0;
		
		memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, 
			(u8*)a + le16_to_cpu(a->name_offset),
			a->name_length * sizeof(ntfschar));
		
		if (le16_to_cpu(a->name_offset) >= 8)
			a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8);

		a->mapping_pairs_offset = 
			cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8);
	}

	/* Update compressed size if required. */
	if (NAttrFullyMapped(na)
	    && (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) {
		s64 new_compr_size;

		new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl);
		if (new_compr_size == -1)
			goto error;
		
		na->compressed_size = new_compr_size;
		a->compressed_size = cpu_to_sle64(new_compr_size);
	}
	/*
	 * Set FILE_NAME dirty flag, to update sparse bit and
	 * allocated size in the index.
	 */
	if (na->type == AT_DATA && na->name == AT_UNNAMED) {
		if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))
			na->ni->allocated_size = na->compressed_size;
		else
			na->ni->allocated_size = na->allocated_size;
		NInoFileNameSetDirty(na->ni);
	}
out:
	return ret;
leave:	ret = -1; goto out;  /* return -1 */
retry:	ret = -2; goto out;
error:  ret = -3; goto out;
}

#define NTFS_VCN_DELETE_MARK -2
/**
 * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs
 */
static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn,
					hole_type holes)
{
	ntfs_attr_search_ctx *ctx;
	ntfs_inode *ni, *base_ni;
	MFT_RECORD *m;
	ATTR_RECORD *a;
	VCN stop_vcn;
	const runlist_element *stop_rl;
	int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1;
	BOOL finished_build;
	BOOL first_updated = FALSE;

retry:
	if (!na || !na->rl) {
		errno = EINVAL;
		ntfs_log_perror("%s: na=%p", __FUNCTION__, na);
		return -1;
	}

	ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", 
		       (unsigned long long)na->ni->mft_no, na->type);

	if (!NAttrNonResident(na)) {
		errno = EINVAL;
		ntfs_log_perror("%s: resident attribute", __FUNCTION__);
		return -1;
	}

#if PARTIAL_RUNLIST_UPDATING
		/*
		 * For a file just been made sparse, we will have
		 * to reformat the first extent, so be sure the
		 * runlist is fully mapped and fully processed.
		 * Same if the file was sparse and is not any more.
		 * Note : not needed if the full runlist is to be processed
		 */
	if ((holes != HOLES_DELAY)
	   && (!NAttrFullyMapped(na) || from_vcn)
	   && !(na->data_flags & ATTR_IS_COMPRESSED)) {
		BOOL changed;

		if (!(na->data_flags & ATTR_IS_SPARSE)) {
			int sparse;
			runlist_element *xrl;

				/*
				 * If attribute was not sparse, we only
				 * have to check whether there is a hole
				 * in the updated region.
				 */
			xrl = na->rl;
			if (xrl->lcn == LCN_RL_NOT_MAPPED)
				xrl++;
			sparse = ntfs_rl_sparse(xrl);
			if (sparse < 0) {
				ntfs_log_error("Could not check whether sparse\n");
				errno = EIO;
				return (-1);
			}
			changed = sparse > 0;
		} else {
				/*
				 * If attribute was sparse, the compressed
				 * size has been maintained, and it gives
				 * and easy way to check whether the
				 * attribute is still sparse.
				 */
			changed = (((na->data_size - 1)
					| (na->ni->vol->cluster_size - 1)) + 1)
				== na->compressed_size;
		}
		if (changed) {
			if (ntfs_attr_map_whole_runlist(na)) {
				ntfs_log_error("Could not map whole for sparse change\n");
				errno = EIO;
				return (-1);
			}
			from_vcn = 0;
		}
	}
#endif
	if (na->ni->nr_extents == -1)
		base_ni = na->ni->base_ni;
	else
		base_ni = na->ni;

	ctx = ntfs_attr_get_search_ctx(base_ni, NULL);
	if (!ctx)
		return -1;

	/* Fill attribute records with new mapping pairs. */
	stop_vcn = 0;
	stop_rl = na->rl;
	finished_build = FALSE;
	while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
				CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) {
		a = ctx->attr;
		m = ctx->mrec;
		if (!a->lowest_vcn)
			first_updated = TRUE;
		/*
		 * If runlist is updating not from the beginning, then set
		 * @stop_vcn properly, i.e. to the lowest vcn of record that
		 * contain @from_vcn. Also we do not need @from_vcn anymore,
		 * set it to 0 to make ntfs_attr_lookup enumerate attributes.
		 */
		if (from_vcn) {
			LCN first_lcn;

			stop_vcn = sle64_to_cpu(a->lowest_vcn);
			from_vcn = 0;
			/*
			 * Check whether the first run we need to update is
			 * the last run in runlist, if so, then deallocate
			 * all attrubute extents starting this one.
			 */
			first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn);
			if (first_lcn == LCN_EINVAL) {
				errno = EIO;
				ntfs_log_perror("Bad runlist");
				goto put_err_out;
			}
			if (first_lcn == LCN_ENOENT ||
					first_lcn == LCN_RL_NOT_MAPPED)
				finished_build = TRUE;
		}

		/*
		 * Check whether we finished mapping pairs build, if so mark
		 * extent as need to delete (by setting highest vcn to
		 * NTFS_VCN_DELETE_MARK (-2), we shall check it later and
		 * delete extent) and continue search.
		 */
		if (finished_build) {
			ntfs_log_trace("Mark attr 0x%x for delete in inode "
				"%lld.\n", (unsigned)le32_to_cpu(a->type),
				(long long)ctx->ntfs_ino->mft_no);
			a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK);
			ntfs_inode_mark_dirty(ctx->ntfs_ino);
			continue;
		}

		switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) {
			case -1: return -1;
			case -2: goto retry;
			case -3: goto put_err_out;
		}

		/*
		 * Determine maximum possible length of mapping pairs,
		 * if we shall *not* expand space for mapping pairs.
		 */
		cur_max_mp_size = le32_to_cpu(a->length) -
				le16_to_cpu(a->mapping_pairs_offset);
		/*
		 * Determine maximum possible length of mapping pairs in the
		 * current mft record, if we shall expand space for mapping
		 * pairs.
		 */
		exp_max_mp_size = le32_to_cpu(m->bytes_allocated) -
				le32_to_cpu(m->bytes_in_use) + cur_max_mp_size;
		/* Get the size for the rest of mapping pairs array. */
		mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, stop_rl,
						stop_vcn, exp_max_mp_size);
		if (mp_size <= 0) {
			ntfs_log_perror("%s: get MP size failed", __FUNCTION__);
			goto put_err_out;
		}
		/* Test mapping pairs for fitting in the current mft record. */
		if (mp_size > exp_max_mp_size) {
			/*
			 * Mapping pairs of $ATTRIBUTE_LIST attribute must fit
			 * in the base mft record. Try to move out other
			 * attributes and try again.
			 */
			if (na->type == AT_ATTRIBUTE_LIST) {
				ntfs_attr_put_search_ctx(ctx);
				if (ntfs_inode_free_space(na->ni, mp_size -
							cur_max_mp_size)) {
					ntfs_log_perror("Attribute list is too "
							"big. Defragment the "
							"volume\n");
					return -1;
				}
				goto retry;
			}

			/* Add attribute list if it isn't present, and retry. */
			if (!NInoAttrList(base_ni)) {
				ntfs_attr_put_search_ctx(ctx);
				if (ntfs_inode_add_attrlist(base_ni)) {
					ntfs_log_perror("Can not add attrlist");
					return -1;
				}
				goto retry;
			}

			/*
			 * Set mapping pairs size to maximum possible for this
			 * mft record. We shall write the rest of mapping pairs
			 * to another MFT records.
			 */
			mp_size = exp_max_mp_size;
		}

		/* Change space for mapping pairs if we need it. */
		if (((mp_size + 7) & ~7) != cur_max_mp_size) {
			if (ntfs_attr_record_resize(m, a,
					le16_to_cpu(a->mapping_pairs_offset) +
					mp_size)) {
				errno = EIO;
				ntfs_log_perror("Failed to resize attribute");
				goto put_err_out;
			}
		}

		/* Update lowest vcn. */
		a->lowest_vcn = cpu_to_sle64(stop_vcn);
		ntfs_inode_mark_dirty(ctx->ntfs_ino);
		if ((ctx->ntfs_ino->nr_extents == -1 ||
					NInoAttrList(ctx->ntfs_ino)) &&
					ctx->attr->type != AT_ATTRIBUTE_LIST) {
			ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn);
			ntfs_attrlist_mark_dirty(ctx->ntfs_ino);
		}

		/*
		 * Generate the new mapping pairs array directly into the
		 * correct destination, i.e. the attribute record itself.
		 */
		if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu(
				a->mapping_pairs_offset), mp_size, na->rl,
				stop_vcn, &stop_rl))
			finished_build = TRUE;
		if (stop_rl)
			stop_vcn = stop_rl->vcn;
		else
			stop_vcn = 0;
		if (!finished_build && errno != ENOSPC) {
			ntfs_log_perror("Failed to build mapping pairs");
			goto put_err_out;
		}
		a->highest_vcn = cpu_to_sle64(stop_vcn - 1);
	}
	/* Check whether error occurred. */
	if (errno != ENOENT) {
		ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__);
		goto put_err_out;
	}
		/*
		 * If the base extent was skipped in the above process,
		 * we still may have to update the sizes.
		 */
	if (!first_updated) {
		le16 spcomp;

		ntfs_attr_reinit_search_ctx(ctx);
		if (!ntfs_attr_lookup(na->type, na->name, na->name_len,
				CASE_SENSITIVE, 0, NULL, 0, ctx)) {
			a = ctx->attr;
			a->allocated_size = cpu_to_sle64(na->allocated_size);
			spcomp = na->data_flags
				& (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
			if (spcomp)
				a->compressed_size = cpu_to_sle64(na->compressed_size);
			if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) {
				na->ni->allocated_size
					= (spcomp
						? na->compressed_size
						: na->allocated_size);
				NInoFileNameSetDirty(na->ni);
			}
		} else {
			ntfs_log_error("Failed to update sizes in base extent\n");
			goto put_err_out;
		}
	}

	/* Deallocate not used attribute extents and return with success. */
	if (finished_build) {
		ntfs_attr_reinit_search_ctx(ctx);
		ntfs_log_trace("Deallocate marked extents.\n");
		while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
				CASE_SENSITIVE, 0, NULL, 0, ctx)) {
			if (sle64_to_cpu(ctx->attr->highest_vcn) !=
							NTFS_VCN_DELETE_MARK)
				continue;
			/* Remove unused attribute record. */
			if (ntfs_attr_record_rm(ctx)) {
				ntfs_log_perror("Could not remove unused attr");
				goto put_err_out;
			}
			ntfs_attr_reinit_search_ctx(ctx);
		}
		if (errno != ENOENT) {
			ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__);
			goto put_err_out;
		}
		ntfs_log_trace("Deallocate done.\n");
		ntfs_attr_put_search_ctx(ctx);
		goto ok;
	}
	ntfs_attr_put_search_ctx(ctx);
	ctx = NULL;

	/* Allocate new MFT records for the rest of mapping pairs. */
	while (1) {
		/* Calculate size of rest mapping pairs. */
		mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol,
						na->rl, stop_vcn, INT_MAX);
		if (mp_size <= 0) {
			ntfs_log_perror("%s: get mp size failed", __FUNCTION__);
			goto put_err_out;
		}
		/* Allocate new mft record. */
		ni = ntfs_mft_record_alloc(na->ni->vol, base_ni);
		if (!ni) {
			ntfs_log_perror("Could not allocate new MFT record");
			goto put_err_out;
		}
		m = ni->mrec;
		/*
		 * If mapping size exceed available space, set them to
		 * possible maximum.
		 */
		cur_max_mp_size = le32_to_cpu(m->bytes_allocated) -
				le32_to_cpu(m->bytes_in_use) -
				(offsetof(ATTR_RECORD, compressed_size) +
				(((na->data_flags & ATTR_COMPRESSION_MASK)
				    || NAttrSparse(na)) ?
				sizeof(a->compressed_size) : 0)) -
				((sizeof(ntfschar) * na->name_len + 7) & ~7);
		if (mp_size > cur_max_mp_size)
			mp_size = cur_max_mp_size;
		/* Add attribute extent to new record. */
		err = ntfs_non_resident_attr_record_add(ni, na->type,
			na->name, na->name_len, stop_vcn, mp_size,
			na->data_flags);
		if (err == -1) {
			err = errno;
			ntfs_log_perror("Could not add attribute extent");
			if (ntfs_mft_record_free(na->ni->vol, ni))
				ntfs_log_perror("Could not free MFT record");
			errno = err;
			goto put_err_out;
		}
		a = (ATTR_RECORD*)((u8*)m + err);

		err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a +
			le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl,
			stop_vcn, &stop_rl);
		if (stop_rl)
			stop_vcn = stop_rl->vcn;
		else
			stop_vcn = 0;
		if (err < 0 && errno != ENOSPC) {
			err = errno;
			ntfs_log_perror("Failed to build MP");
			if (ntfs_mft_record_free(na->ni->vol, ni))
				ntfs_log_perror("Couldn't free MFT record");
			errno = err;
			goto put_err_out;
		}
		a->highest_vcn = cpu_to_sle64(stop_vcn - 1);
		ntfs_inode_mark_dirty(ni);
		/* All mapping pairs has been written. */
		if (!err)
			break;
	}
ok:
	ret = 0;
out:
	return ret;
put_err_out:
	if (ctx)
		ntfs_attr_put_search_ctx(ctx);
	goto out;
}
#undef NTFS_VCN_DELETE_MARK

/**
 * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute
 * @na:		non-resident ntfs open attribute for which we need update
 * @from_vcn:	update runlist starting this VCN
 *
 * Build mapping pairs from @na->rl and write them to the disk. Also, this
 * function updates sparse bit, allocated and compressed size (allocates/frees
 * space for this field if required).
 *
 * @na->allocated_size should be set to correct value for the new runlist before
 * call to this function. Vice-versa @na->compressed_size will be calculated and
 * set to correct value during this function.
 *
 * FIXME: This function does not update sparse bit and compressed size correctly
 * if called with @from_vcn != 0.
 *
 * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define.
 *
 * On success return 0 and on error return -1 with errno set to the error code.
 * The following error codes are defined:
 *	EINVAL - Invalid arguments passed.
 *	ENOMEM - Not enough memory to complete operation.
 *	ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST
 *		 or there is no free MFT records left to allocate.
 */
int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn)
{
	int ret; 
	
	ntfs_log_enter("Entering\n");
	ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK);
	ntfs_log_leave("\n");
	return ret;
}

/**
 * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute
 * @na:		non-resident ntfs attribute to shrink
 * @newsize:	new size (in bytes) to which to shrink the attribute
 *
 * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
 *
 * On success return 0 and on error return -1 with errno set to the error code.
 * The following error codes are defined:
 *	ENOMEM	- Not enough memory to complete operation.
 *	ERANGE	- @newsize is not valid for the attribute type of @na.
 */
static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize)
{
	ntfs_volume *vol;
	ntfs_attr_search_ctx *ctx;
	VCN first_free_vcn;
	s64 nr_freed_clusters;
	int err;

	ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", (unsigned long long)
		       na->ni->mft_no, na->type, (long long)newsize);

	vol = na->ni->vol;

	/*
	 * Check the attribute type and the corresponding minimum size
	 * against @newsize and fail if @newsize is too small.
	 */
	if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
		if (errno == ERANGE) {
			ntfs_log_trace("Eeek! Size bounds check failed. "
					"Aborting...\n");
		} else if (errno == ENOENT)
			errno = EIO;
		return -1;
	}

	/* The first cluster outside the new allocation. */
	if (na->data_flags & ATTR_COMPRESSION_MASK)
		/*
		 * For compressed files we must keep full compressions blocks,
		 * but currently we do not decompress/recompress the last
		 * block to truncate the data, so we may leave more allocated
		 * clusters than really needed.
		 */
		first_free_vcn = (((newsize - 1)
				 | (na->compression_block_size - 1)) + 1)
				   >> vol->cluster_size_bits;
	else
		first_free_vcn = (newsize + vol->cluster_size - 1) >>
				vol->cluster_size_bits;
	/*
	 * Compare the new allocation with the old one and only deallocate
	 * clusters if there is a change.
	 */
	if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) {
		if (ntfs_attr_map_whole_runlist(na)) {
			ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist "
					"failed.\n");
			return -1;
		}
		/* Deallocate all clusters starting with the first free one. */
		nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn,
				-1);
		if (nr_freed_clusters < 0) {
			ntfs_log_trace("Eeek! Freeing of clusters failed. "
					"Aborting...\n");
			return -1;
		}

		/* Truncate the runlist itself. */
		if (ntfs_rl_truncate(&na->rl, first_free_vcn)) {
			/*
			 * Failed to truncate the runlist, so just throw it
			 * away, it will be mapped afresh on next use.
			 */
			free(na->rl);
			na->rl = NULL;
			ntfs_log_trace("Eeek! Run list truncation failed.\n");
			return -1;
		}

		/* Prepare to mapping pairs update. */
		na->allocated_size = first_free_vcn << vol->cluster_size_bits;
		/* Write mapping pairs for new runlist. */
		if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) {
			ntfs_log_trace("Eeek! Mapping pairs update failed. "
					"Leaving inconstant metadata. "
					"Run chkdsk.\n");
			return -1;
		}
	}

	/* Get the first attribute record. */
	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx)
		return -1;

	if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
			0, NULL, 0, ctx)) {
		err = errno;
		if (err == ENOENT)
			err = EIO;
		ntfs_log_trace("Eeek! Lookup of first attribute extent failed. "
				"Leaving inconstant metadata.\n");
		goto put_err_out;
	}

	/* Update data and initialized size. */
	na->data_size = newsize;
	ctx->attr->data_size = cpu_to_sle64(newsize);
	if (newsize < na->initialized_size) {
		na->initialized_size = newsize;
		ctx->attr->initialized_size = cpu_to_sle64(newsize);
	}
	/* Update data size in the index. */
	if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
		if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) {
			na->ni->data_size = na->data_size;
			na->ni->allocated_size = na->allocated_size;
			set_nino_flag(na->ni,KnownSize);
		}
	} else {
		if (na->type == AT_DATA && na->name == AT_UNNAMED) {
			na->ni->data_size = na->data_size;
			NInoFileNameSetDirty(na->ni);
		}
	}

	/* If the attribute now has zero size, make it resident. */
	if (!newsize) {
		if (ntfs_attr_make_resident(na, ctx)) {
			/* If couldn't make resident, just continue. */
			if (errno != EPERM)
				ntfs_log_error("Failed to make attribute "
						"resident. Leaving as is...\n");
		}
	}

	/* Set the inode dirty so it is written out later. */
	ntfs_inode_mark_dirty(ctx->ntfs_ino);
	/* Done! */
	ntfs_attr_put_search_ctx(ctx);
	return 0;
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
	errno = err;
	return -1;
}

/**
 * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute
 * @na:		non-resident ntfs attribute to expand
 * @newsize:	new size (in bytes) to which to expand the attribute
 *
 * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes,
 * by allocating new clusters.
 *
 * On success return 0 and on error return -1 with errno set to the error code.
 * The following error codes are defined:
 *	ENOMEM - Not enough memory to complete operation.
 *	ERANGE - @newsize is not valid for the attribute type of @na.
 *	ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST.
 */
static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize,
					hole_type holes)
{
	LCN lcn_seek_from;
	VCN first_free_vcn;
	ntfs_volume *vol;
	ntfs_attr_search_ctx *ctx;
	runlist *rl, *rln;
	s64 org_alloc_size;
	int err;

	ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n",
			(unsigned long long)na->ni->mft_no, na->type,
			(long long)newsize, (long long)na->data_size);

	vol = na->ni->vol;

	/*
	 * Check the attribute type and the corresponding maximum size
	 * against @newsize and fail if @newsize is too big.
	 */
	if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
		if (errno == ENOENT)
			errno = EIO;
		ntfs_log_perror("%s: bounds check failed", __FUNCTION__);
		return -1;
	}

	if (na->type == AT_DATA)
		NAttrSetDataAppending(na);
	/* Save for future use. */
	org_alloc_size = na->allocated_size;
	/* The first cluster outside the new allocation. */
	first_free_vcn = (newsize + vol->cluster_size - 1) >>
			vol->cluster_size_bits;
	/*
	 * Compare the new allocation with the old one and only allocate
	 * clusters if there is a change.
	 */
	if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) {
#if PARTIAL_RUNLIST_UPDATING
		s64 start_update;

		/*
		 * Update from the last previously allocated run,
		 * as we may have to expand an existing hole.
		 */
		start_update = na->allocated_size >> vol->cluster_size_bits;
		if (start_update)
			start_update--;
		if (ntfs_attr_map_partial_runlist(na, start_update)) {
			ntfs_log_perror("failed to map partial runlist");
			return -1;
		}
#else
		if (ntfs_attr_map_whole_runlist(na)) {
			ntfs_log_perror("ntfs_attr_map_whole_runlist failed");
			return -1;
		}
#endif

		/*
		 * If we extend $DATA attribute on NTFS 3+ volume, we can add
		 * sparse runs instead of real allocation of clusters.
		 */
		if ((na->type == AT_DATA) && (vol->major_ver >= 3)
					 && (holes != HOLES_NO)) {
			rl = ntfs_malloc(0x1000);
			if (!rl)
				return -1;
			
			rl[0].vcn = (na->allocated_size >>
					vol->cluster_size_bits);
			rl[0].lcn = LCN_HOLE;
			rl[0].length = first_free_vcn -
				(na->allocated_size >> vol->cluster_size_bits);
			rl[1].vcn = first_free_vcn;
			rl[1].lcn = LCN_ENOENT;
			rl[1].length = 0;
		} else {
			/*
			 * Determine first after last LCN of attribute.
			 * We will start seek clusters from this LCN to avoid
			 * fragmentation.  If there are no valid LCNs in the
			 * attribute let the cluster allocator choose the
			 * starting LCN.
			 */
			lcn_seek_from = -1;
			if (na->rl->length) {
				/* Seek to the last run list element. */
				for (rl = na->rl; (rl + 1)->length; rl++)
					;
				/*
				 * If the last LCN is a hole or similar seek
				 * back to last valid LCN.
				 */
				while (rl->lcn < 0 && rl != na->rl)
					rl--;
				/*
				 * Only set lcn_seek_from it the LCN is valid.
				 */
				if (rl->lcn >= 0)
					lcn_seek_from = rl->lcn + rl->length;
			}

			rl = ntfs_cluster_alloc(vol, na->allocated_size >>
					vol->cluster_size_bits, first_free_vcn -
					(na->allocated_size >>
					vol->cluster_size_bits), lcn_seek_from,
					DATA_ZONE);
			if (!rl) {
				ntfs_log_perror("Cluster allocation failed "
						"(%lld)",
						(long long)first_free_vcn -
						((long long)na->allocated_size >>
						 vol->cluster_size_bits));
				return -1;
			}
		}

		/* Append new clusters to attribute runlist. */
		rln = ntfs_runlists_merge(na->rl, rl);
		if (!rln) {
			/* Failed, free just allocated clusters. */
			err = errno;
			ntfs_log_perror("Run list merge failed");
			ntfs_cluster_free_from_rl(vol, rl);
			free(rl);
			errno = err;
			return -1;
		}
		na->rl = rln;

		/* Prepare to mapping pairs update. */
		na->allocated_size = first_free_vcn << vol->cluster_size_bits;
#if PARTIAL_RUNLIST_UPDATING
		/*
		 * Write mapping pairs for new runlist, unless this is
		 * a temporary state before appending data.
		 * If the update is not done, we must be sure to do
		 * it later, and to get to a clean state even on errors.
		 */
		if ((holes != HOLES_DELAY)
		   && ntfs_attr_update_mapping_pairs_i(na, start_update,
					holes)) {
#else
		/* Write mapping pairs for new runlist. */
		if (ntfs_attr_update_mapping_pairs(na, 0)) {
#endif
			err = errno;
			ntfs_log_perror("Mapping pairs update failed");
			goto rollback;
		}
	}

	ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
	if (!ctx) {
		err = errno;
		if (na->allocated_size == org_alloc_size) {
			errno = err;
			return -1;
		} else
			goto rollback;
	}

	if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
			0, NULL, 0, ctx)) {
		err = errno;
		ntfs_log_perror("Lookup of first attribute extent failed");
		if (err == ENOENT)
			err = EIO;
		if (na->allocated_size != org_alloc_size) {
			ntfs_attr_put_search_ctx(ctx);
			goto rollback;
		} else
			goto put_err_out;
	}

	/* Update data size. */
	na->data_size = newsize;
	ctx->attr->data_size = cpu_to_sle64(newsize);
	/* Update data size in the index. */
	if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
		if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) {
			na->ni->data_size = na->data_size;
			na->ni->allocated_size = na->allocated_size;
			set_nino_flag(na->ni,KnownSize);
		}
	} else {
		if (na->type == AT_DATA && na->name == AT_UNNAMED) {
			na->ni->data_size = na->data_size;
			NInoFileNameSetDirty(na->ni);
		}
	}
	/* Set the inode dirty so it is written out later. */
	ntfs_inode_mark_dirty(ctx->ntfs_ino);
	/* Done! */
	ntfs_attr_put_search_ctx(ctx);
	return 0;
rollback:
	/* Free allocated clusters. */
	if (ntfs_cluster_free(vol, na, org_alloc_size >>
			vol->cluster_size_bits, -1) < 0) {
		err = EIO;
		ntfs_log_perror("Leaking clusters");
	}
	/* Now, truncate the runlist itself. */
	if (ntfs_rl_truncate(&na->rl, org_alloc_size >>
			vol->cluster_size_bits)) {
		/*
		 * Failed to truncate the runlist, so just throw it away, it
		 * will be mapped afresh on next use.
		 */
		free(na->rl);
		na->rl = NULL;
		ntfs_log_perror("Couldn't truncate runlist. Rollback failed");
	} else {
		/* Prepare to mapping pairs update. */
		na->allocated_size = org_alloc_size;
		/* Restore mapping pairs. */
		if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >>
					vol->cluster_size_bits*/)) {
			ntfs_log_perror("Failed to restore old mapping pairs");
		}
	}
	errno = err;
	return -1;
put_err_out:
	ntfs_attr_put_search_ctx(ctx);
	errno = err;
	return -1;
}


static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize,
				hole_type holes)
{
	int ret; 
	
	ntfs_log_enter("Entering\n");
	ret = ntfs_non_resident_attr_expand_i(na, newsize, holes);
	ntfs_log_leave("\n");
	return ret;
}

/**
 * ntfs_attr_truncate - resize an ntfs attribute
 * @na:		open ntfs attribute to resize
 * @newsize:	new size (in bytes) to which to resize the attribute
 * @holes:	how to create a hole if expanding
 *
 * Change the size of an open ntfs attribute @na to @newsize bytes. If the
 * attribute is made bigger and the attribute is resident the newly
 * "allocated" space is cleared and if the attribute is non-resident the
 * newly allocated space is marked as not initialised and no real allocation
 * on disk is performed.
 *
 * On success return 0.
 * On error return values are:
 * 	STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT
 * 	STATUS_ERROR - otherwise
 * The following error codes are defined:
 *	EINVAL	   - Invalid arguments were passed to the function.
 *	EOPNOTSUPP - The desired resize is not implemented yet.
 * 	EACCES     - Encrypted attribute.
 */
static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize,
					hole_type holes)
{
	int ret = STATUS_ERROR;
	s64 fullsize;
	BOOL compressed;

	if (!na || newsize < 0 ||
			(na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) {
		ntfs_log_trace("Invalid arguments passed.\n");
		errno = EINVAL;
		return STATUS_ERROR;
	}

	ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n",
		       (unsigned long long)na->ni->mft_no, na->type, 
		       (long long)newsize);

	if (na->data_size == newsize) {
		ntfs_log_trace("Size is already ok\n");
		ret = STATUS_OK;
		goto out;
	}
	/*
	 * Encrypted attributes are not supported. We return access denied,
	 * which is what Windows NT4 does, too.
	 */
	if (na->data_flags & ATTR_IS_ENCRYPTED) {
		errno = EACCES;
		ntfs_log_trace("Cannot truncate encrypted attribute\n");
		goto out;
	}
	/*
	 * TODO: Implement making handling of compressed attributes.
	 * Currently we can only expand the attribute or delete it,
	 * and only for ATTR_IS_COMPRESSED. This is however possible
	 * for resident attributes when there is no open fuse context
	 * (important case : $INDEX_ROOT:$I30)
	 */
	compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
			 != const_cpu_to_le16(0);
	if (compressed
	   && NAttrNonResident(na)
	   && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) {
		errno = EOPNOTSUPP;
		ntfs_log_perror("Failed to truncate compressed attribute");
		goto out;
	}
	if (NAttrNonResident(na)) {
		/*
		 * For compressed data, the last block must be fully
		 * allocated, and we do not know the size of compression
		 * block until the attribute has been made non-resident.
		 * Moreover we can only process a single compression
		 * block at a time (from where we are about to write),
		 * so we silently do not allocate more.
		 *
		 * Note : do not request upsizing of compressed files
		 * unless being able to face the consequences !
		 */
		if (compressed && newsize && (newsize > na->data_size))
			fullsize = (na->initialized_size
				 | (na->compression_block_size - 1)) + 1;
		else
			fullsize = newsize;
		if (fullsize > na->data_size)
			ret = ntfs_non_resident_attr_expand(na, fullsize,
								holes);
		else
			ret = ntfs_non_resident_attr_shrink(na, fullsize);
	} else
		ret = ntfs_resident_attr_resize(na, newsize);
out:	
	ntfs_log_leave("Return status %d\n", ret);
	return ret;
}

/*
 *		Resize an attribute, creating a hole if relevant
 */

int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
{
	int r;

	r = ntfs_attr_truncate_i(na, newsize, HOLES_OK);
	NAttrClearDataAppending(na);
	NAttrClearBeingNonResident(na);
	return (r);
}

/*
 *		Resize an attribute, avoiding hole creation
 */

int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize)
{
	return (ntfs_attr_truncate_i(na, newsize, HOLES_NO));
}

/*
 *		Stuff a hole in a compressed file
 *
 *	An unallocated hole must be aligned on compression block size.
 *	If needed current block and target block are stuffed with zeroes.
 *
 *	Returns 0 if succeeded,
 *		-1 if it failed (as explained in errno)
 */

static int stuff_hole(ntfs_attr *na, const s64 pos)
{
	s64 size;
	s64 begin_size;
	s64 end_size;
	char *buf;
	int ret;

	ret = 0;
		/*
		 * If the attribute is resident, the compression block size
		 * is not defined yet and we can make no decision.
		 * So we first try resizing to the target and if the
		 * attribute is still resident, we're done
		 */
	if (!NAttrNonResident(na)) {
		ret = ntfs_resident_attr_resize(na, pos);
		if (!ret && !NAttrNonResident(na))
			na->initialized_size = na->data_size = pos;
	}
	if (!ret && NAttrNonResident(na)) {
			/* does the hole span over several compression block ? */
		if ((pos ^ na->initialized_size)
				& ~(na->compression_block_size - 1)) {
			begin_size = ((na->initialized_size - 1)
					| (na->compression_block_size - 1))
					+ 1 - na->initialized_size;
			end_size = pos & (na->compression_block_size - 1);
			size = (begin_size > end_size ? begin_size : end_size);
		} else {
			/* short stuffing in a single compression block */
			begin_size = size = pos - na->initialized_size;
			end_size = 0;
		}
		if (size)
			buf = (char*)ntfs_malloc(size);
		else
			buf = (char*)NULL;
		if (buf || !size) {
			memset(buf,0,size);
				/* stuff into current block */
			if (begin_size
			    && (ntfs_attr_pwrite(na,
				na->initialized_size, begin_size, buf)
				   != begin_size))
				ret = -1;
				/* create an unstuffed hole */
			if (!ret
			    && ((na->initialized_size + end_size) < pos)
			    && ntfs_non_resident_attr_expand(na,
					pos - end_size, HOLES_OK))
				ret = -1;
			else 
				na->initialized_size
				    = na->data_size = pos - end_size;
				/* stuff into the target block */
			if (!ret && end_size
			    && (ntfs_attr_pwrite(na, 
				na->initialized_size, end_size, buf)
				    != end_size))
				ret = -1;
			if (buf)
				free(buf);
		} else
			ret = -1;
	}
		/* make absolutely sure we have reached the target */
	if (!ret && (na->initialized_size != pos)) {
		ntfs_log_error("Failed to stuff a compressed file"
			"target %lld reached %lld\n",
			(long long)pos, (long long)na->initialized_size);
		errno = EIO;
		ret = -1;
	}
	return (ret);
}

/**
 * ntfs_attr_readall - read the entire data from an ntfs attribute
 * @ni:		open ntfs inode in which the ntfs attribute resides
 * @type:	attribute type
 * @name:	attribute name in little endian Unicode or AT_UNNAMED or NULL
 * @name_len:	length of attribute @name in Unicode characters (if @name given)
 * @data_size:	if non-NULL then store here the data size 
 *
 * This function will read the entire content of an ntfs attribute.
 * If @name is AT_UNNAMED then look specifically for an unnamed attribute.
 * If @name is NULL then the attribute could be either named or not. 
 * In both those cases @name_len is not used at all.
 *
 * On success a buffer is allocated with the content of the attribute 
 * and which needs to be freed when it's not needed anymore. If the
 * @data_size parameter is non-NULL then the data size is set there.
 *
 * On error NULL is returned with errno set to the error code.
 */
void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
			ntfschar *name, u32 name_len, s64 *data_size)
{
	ntfs_attr *na;
	void *data, *ret = NULL;
	s64 size;
	
	ntfs_log_enter("Entering\n");
	
	na = ntfs_attr_open(ni, type, name, name_len);
	if (!na) {
		ntfs_log_perror("ntfs_attr_open failed");
		goto err_exit;
	}
	data = ntfs_malloc(na->data_size);
	if (!data)
		goto out;
	
	size = ntfs_attr_pread(na, 0, na->data_size, data);
	if (size != na->data_size) {
		ntfs_log_perror("ntfs_attr_pread failed");
		free(data);
		goto out;
	}
	ret = data;
	if (data_size)
		*data_size = size;
out:
	ntfs_attr_close(na);
err_exit:
	ntfs_log_leave("\n");
	return ret;
}

/*
 *		Read some data from a data attribute
 *
 *	Returns the amount of data read, negative if there was an error
 */

int ntfs_attr_data_read(ntfs_inode *ni,
		ntfschar *stream_name, int stream_name_len,
		char *buf, size_t size, off_t offset)
{
	ntfs_attr *na = NULL;
	int res, total = 0;

	na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
	if (!na) {
		res = -errno;
		goto exit;
	}
	if ((size_t)offset < (size_t)na->data_size) {
		if (offset + size > (size_t)na->data_size)
			size = na->data_size - offset;
		while (size) {
			res = ntfs_attr_pread(na, offset, size, buf + total);
			if ((off_t)res < (off_t)size)
				ntfs_log_perror("ntfs_attr_pread partial read "
					"(%lld : %lld <> %d)",
					(long long)offset,
					(long long)size, res);
			if (res <= 0) {
				res = -errno;
				goto exit;
			}
			size -= res;
			offset += res;
			total += res;
		}
	}
	res = total;
exit:
	if (na)
		ntfs_attr_close(na);
	return res;
}


/*
 *		Write some data into a data attribute
 *
 *	Returns the amount of data written, negative if there was an error
 */

int ntfs_attr_data_write(ntfs_inode *ni, ntfschar *stream_name,
		int stream_name_len, const char *buf, size_t size, off_t offset)
{
	ntfs_attr *na = NULL;
	int res, total = 0;

	na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
	if (!na) {
		res = -errno;
		goto exit;
	}
	while (size) {
		res = ntfs_attr_pwrite(na, offset, size, buf + total);
		if (res < (s64)size)
			ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: "
				"%lld <> %d)", (long long)offset,
				(long long)size, res);
		if (res <= 0) {
			res = -errno;
			goto exit;
		}
		size -= res;
		offset += res;
		total += res;
	}
	res = total;
exit:
	if (na)
		ntfs_attr_close(na);
	return res;
}


int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, const ntfschar *name,
		    u32 name_len)
{
	ntfs_attr_search_ctx *ctx;
	int ret;
	
	ntfs_log_trace("Entering\n");
	
	ctx = ntfs_attr_get_search_ctx(ni, NULL);
	if (!ctx)
		return 0;
	
	ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0,
			       ctx);

	ntfs_attr_put_search_ctx(ctx);
	
	return !ret;
}

int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, 
		     u32 name_len)
{
	ntfs_attr *na;
	int ret;

	ntfs_log_trace("Entering\n");
	
	if (!ni) {
		ntfs_log_error("%s: NULL inode pointer", __FUNCTION__);
		errno = EINVAL;
		return -1;
	}
	
	na = ntfs_attr_open(ni, type, name, name_len);
	if (!na) {
			/* do not log removal of non-existent stream */
		if (type != AT_DATA) {
			ntfs_log_perror("Failed to open attribute 0x%02x of inode "
				"0x%llx", type, (unsigned long long)ni->mft_no);
		}
		return -1;
	}
	
	ret = ntfs_attr_rm(na);
	if (ret)
		ntfs_log_perror("Failed to remove attribute 0x%02x of inode "
				"0x%llx", type, (unsigned long long)ni->mft_no);
	ntfs_attr_close(na);
	
	return ret;
}

/* Below macros are 32-bit ready. */
#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \
		      (((x) >> 2) & 0x33333333) - \
		      (((x) >> 3) & 0x11111111))
#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255)

static u8 *ntfs_init_lut256(void)
{
	int i;
	u8 *lut;
	
	lut = ntfs_malloc(256);
	if (lut)
		for(i = 0; i < 256; i++)
			*(lut + i) = 8 - BITCOUNT(i);
	return lut;
}

s64 ntfs_attr_get_free_bits(ntfs_attr *na)
{
	u8 *buf, *lut;
	s64 br      = 0;
	s64 total   = 0;
	s64 nr_free = 0;

	lut = ntfs_init_lut256();
	if (!lut)
		return -1;
	
	buf = ntfs_malloc(65536);
	if (!buf)
		goto out;

	while (1) {
		u32 *p;
		br = ntfs_attr_pread(na, total, 65536, buf);
		if (br <= 0)
			break;
		total += br;
		p = (u32 *)buf + br / 4 - 1;
		for (; (u8 *)p >= buf; p--) {
			nr_free += lut[ *p        & 255] + 
			           lut[(*p >>  8) & 255] + 
			           lut[(*p >> 16) & 255] + 
			           lut[(*p >> 24)      ];
		}
		switch (br % 4) {
			case 3:  nr_free += lut[*(buf + br - 3)];
			case 2:  nr_free += lut[*(buf + br - 2)];
			case 1:  nr_free += lut[*(buf + br - 1)];
		}
	}
	free(buf);
out:
	free(lut);
	if (!total || br < 0)
		return -1;
	return nr_free;
}
