root/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c
/**
 * 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-2021 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

#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", le32_to_cpu(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;
                        s64 intlth;
                        /*
                         * 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.
                         *
                         * For safety, limit the amount to read to the
                         * needed size, knowing that the whole attribute
                         * size has been checked to be <= 0x40000.
                         */
                        intlth = (sle64_to_cpu(a->data_size) - total
                                        + vol->cluster_size - 1)
                                        >> vol->cluster_size_bits;
                        if (rl[i].length < intlth)
                                intlth = rl[i].length;
                        intbuf = (u8*)ntfs_malloc(intlth
                                                << 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,
                                        intlth << vol->cluster_size_bits,
                                        intbuf);
                        if (r != intlth << vol->cluster_size_bits) {
#define ESTR "Error reading attribute value"
                                if (r == -1)
                                        ntfs_log_perror(ESTR);
                                else if (r < intlth <<
                                                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, le32_to_cpu(type));
        
        if (!ni || !ni->vol || !ni->mrec) {
                errno = EINVAL;
                goto out;
        }
        na = ntfs_calloc(sizeof(ntfs_attr));
        if (!na)
                goto out;
        if (!name_len)
                name = (ntfschar*)NULL;
        if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) {
                /* A null char leads to a short name and unallocated bytes */
                if (ntfs_ucsnlen(name, name_len) != name_len) {
                        ntfs_log_error("Null character in attribute name"
                                " of inode %lld\n",(long long)ni->mft_no);
                        goto err_out;
                }
                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) {
                        ntfschar *attr_name;

                        attr_name = (ntfschar*)((u8*)a
                                        + le16_to_cpu(a->name_offset));
                        /* A null character leads to illegal memory access */
                        if (ntfs_ucsnlen(attr_name, a->name_length)
                                                != a->name_length) {
                                ntfs_log_error("Null character in attribute"
                                        " name in inode %lld\n",
                                        (long long)ni->mft_no);
                                goto put_err_out;
                        }
                        name = ntfs_ucsndup(attr_name, 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 = const_cpu_to_le16(0);

        if ((type == AT_DATA)
           && (a->non_resident ? !a->initialized_size : !a->value_length)) {
                /*
                 * 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,
                                le16_to_cpu(a->flags), le32_to_cpu(na->ni->flags));
                goto put_err_out;
        }

        if (a->non_resident) {
                if (((a->flags & ATTR_COMPRESSION_MASK)
                        || a->compression_unit)
                    && (ni->vol->major_ver < 3)) {
                        errno = EIO;
                        ntfs_log_perror("Compressed inode %lld not allowed"
                                        " on NTFS %d.%d",
                                        (unsigned long long)ni->mft_no,
                                        ni->vol->major_ver,
                                        ni->vol->major_ver);
                        goto put_err_out;
                }
                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, le32_to_cpu(type));
                        goto put_err_out;
                }
                if ((a->flags & ATTR_COMPRESSION_MASK)
                                 && (a->compression_unit
                                        != STANDARD_COMPRESSION_UNIT)) {
                        errno = EIO;
                        ntfs_log_perror("Compressed inode %lld attr 0x%lx has "
                                        "an unsupported compression unit %d",
                                        (unsigned long long)ni->mft_no,
                                        (long)le32_to_cpu(type),
                                        (int)a->compression_unit);
                        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, le32_to_cpu(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)
{
        VCN last_vcn;
        VCN highest_vcn;
        VCN needed;
        runlist_element *rl;
        ATTR_RECORD *a;
        BOOL startseen;
        ntfs_attr_search_ctx *ctx;
        BOOL done;
        BOOL newrunlist;

        if (NAttrFullyMapped(na))
                return 0;

        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;
        done = FALSE;
        rl = (runlist_element*)NULL;
        do {
                newrunlist = FALSE;
                /* 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. */
                        if (ntfs_rl_vcn_to_lcn(na->rl, needed)
                                                == LCN_RL_NOT_MAPPED) {
                                rl = ntfs_mapping_pairs_decompress(na->ni->vol,
                                        a, na->rl);
                                newrunlist = TRUE;
                        } else
                                rl = na->rl;
                        if (rl) {
                                na->rl = rl;
                                highest_vcn = sle64_to_cpu(a->highest_vcn);
                                if (highest_vcn < needed) {
                                /* corruption detection on unchanged runlists */
                                        if (newrunlist
                                            && ((highest_vcn + 1) < last_vcn)) {
                                                ntfs_log_error("Corrupt attribute list\n");
                                                rl = (runlist_element*)NULL;
                                                errno = EIO;
                                        }
                                        done = TRUE;
                                }
                                needed = highest_vcn + 1;
                                if (!a->lowest_vcn)
                                        startseen = TRUE;
                        }
                } else {
                        done = TRUE;
                }
        } while (rl && !done && (needed < last_vcn));
        ntfs_attr_put_search_ctx(ctx);
                /*
                 * Make sure we reached the end, unless the last
                 * runlist was modified earlier (using HOLES_DELAY
                 * leads to have a visibility over attributes which
                 * have not yet been fully updated)
                 */
        if (done && newrunlist && (needed < last_vcn)) {
                ntfs_log_error("End of runlist not reached\n");
                rl = (runlist_element*)NULL;
                errno = EIO;
        }
                /* 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;
        int not_mapped;

        ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n",
                       (unsigned long long)na->ni->mft_no, le32_to_cpu(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;

                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;
        }
                /*
                 * Cannot check highest_vcn when the last runlist has
                 * been modified earlier, as runlists and sizes may be
                 * updated without highest_vcn being in sync, when
                 * HOLES_DELAY is used
                 */
        if (not_mapped && 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, le32_to_cpu(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, le32_to_cpu(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 {
                                *(le16*)((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,
                       le32_to_cpu(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;

        if (na->ni->mft_no == FILE_Bitmap) {
                /*
                 * Filling a hole in the main bitmap implies allocating
                 * clusters, which is likely to imply updating the
                 * bitmap in a cluster being allocated.
                 * Not supported now, could lead to endless recursions.
                 */
                ntfs_log_error("Corrupt $BitMap not fully allocated\n");
                errno = EIO;
                goto err_out;
        }
        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) {
                if (ntfs_attr_map_whole_runlist(na))
                        goto err_out;
        } else {
                /* make sure the run ahead of hole is mapped */
                if ((*rl)->lcn == LCN_HOLE) {
                        if (ntfs_attr_map_partial_runlist(na,
                                (cur_vcn ? cur_vcn - 1 : cur_vcn)))
                                        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);
        NAttrSetRunlistDirty(na);
                /*
                 * 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);
                        }
                }
        NAttrSetRunlistDirty(na);
        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;
                                NAttrSetRunlistDirty(na);
                        }
                }
                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.
 */
static s64 ntfs_attr_pwrite_i(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;

        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 (compressed && !NAttrDataAppending(na)) {
                        if (ntfs_attr_map_whole_runlist(na))
                                goto err_out;
                } else {
                        VCN block_begin;

                        if (NAttrDataAppending(na)
                            || (pos < na->initialized_size))
                                block_begin = pos >> vol->cluster_size_bits;
                        else
                                block_begin = na->initialized_size >> 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
                        if (ntfs_attr_map_whole_runlist(na))
                                goto err_out;
#endif
                /*
                 * 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 (NAttrRunlistDirty(na)) {
                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:    
        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. */
        if (NAttrRunlistDirty(na))
                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;
}

s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
{
        s64 total;
        s64 written;

        ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count "
                       "0x%llx.\n", (long long)na->ni->mft_no, le32_to_cpu(na->type),
                       (long long)pos, (long long)count);
        
        total = 0;
        if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
                errno = EINVAL;
                written = -1;
                ntfs_log_perror("%s", __FUNCTION__);
                goto out;
        }

                /*
                 * Compressed attributes may be written partially, so
                 * we may have to iterate.
                 */
        do {
                written = ntfs_attr_pwrite_i(na, pos + total,
                                count - total, (const u8*)b + total);
                if (written > 0)
                        total += written;
        } while ((written > 0) && (total < count));
out :
        ntfs_log_leave("\n");
        return (total > 0 ? total : written);
}


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",
                        (unsigned long long)na->ni->mft_no,
                        le32_to_cpu(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, le32_to_cpu(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, le32_to_cpu(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;
        ptrdiff_t offs;
        ptrdiff_t space;
        u32 upcase_len;

        ntfs_log_trace("attribute type 0x%x.\n", le32_to_cpu(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))) {
                /*
                 * Make sure the attribute fully lies within the MFT record
                 * and we can safely access its minimal fields.
                 */
                offs = p2n(a) - p2n(ctx->mrec);
                space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs;
                if ((offs < 0)
                    || (((space < (ptrdiff_t)offsetof(ATTR_RECORD,
                                                resident_end))
                        || (space < (ptrdiff_t)le32_to_cpu(a->length)))
                            && ((space < 4) || (a->type != AT_END))))
                        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 (a->name_length
                            && ((le16_to_cpu(a->name_offset)
                                        + a->name_length * sizeof(ntfschar))
                                        > le32_to_cpu(a->length))) {
                                ntfs_log_error("Corrupt attribute name"
                                        " in MFT record %lld\n",
                                        (long long)ctx->ntfs_ino->mft_no);
                                break;
                        }
                        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;
        ptrdiff_t offs;
        ptrdiff_t space;
        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, le32_to_cpu(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 {
                        /* Check for small entry */
                if (((p2n(al_end) - p2n(ctx->al_entry))
                                < (long)offsetof(ATTR_LIST_ENTRY, name))
                    || (le16_to_cpu(ctx->al_entry->length) & 7)
                    || (le16_to_cpu(ctx->al_entry->length)
                                < offsetof(ATTR_LIST_ENTRY, name)))
                        goto corrupt;

                al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +
                                le16_to_cpu(ctx->al_entry->length));
                if ((u8*)al_entry == al_end)
                        goto not_found;
                        /* Preliminary check for small entry */
                if ((p2n(al_end) - p2n(al_entry))
                                < (long)offsetof(ATTR_LIST_ENTRY, name))
                        goto corrupt;
                /*
                 * 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 ((((u8*)al_entry + offsetof(ATTR_LIST_ENTRY, name)) > al_end)
                    || ((u8*)al_entry + le16_to_cpu(al_entry->length) > al_end)
                    || (le16_to_cpu(al_entry->length) & 7)
                    || (le16_to_cpu(al_entry->length)
                                < offsetof(ATTR_LIST_ENTRY, name_length))
                    || (al_entry->name_length
                        && ((u8*)al_entry + al_entry->name_offset
                                + al_entry->name_length * sizeof(ntfschar))
                                > al_end))
                        break; /* corrupt */

                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. */
                                if (!vol->mft_na) {
                                        ntfs_log_perror("$MFT not ready for "
                                            "opening an extent to inode %lld\n",
                                            (long long)base_ni->mft_no);
                                        break;
                                }
                                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:
                /*
                 * Make sure the attribute fully lies within the MFT record
                 * and we can safely access its minimal fields.
                 */
                offs = p2n(a) - p2n(ctx->mrec);
                space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs;
                if (offs < 0)
                        break;
                if ((space >= 4) && (a->type == AT_END))
                        continue;
                if ((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end))
                    || (space < (ptrdiff_t)le32_to_cpu(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;
        }
corrupt :
        if (ni != base_ni) {
                ctx->ntfs_ino = base_ni;
                ctx->mrec = ctx->base_mrec;
                ctx->attr = ctx->base_attr;
        }
        errno = EIO;
        ntfs_log_error("Corrupt attribute list entry in MFT record %lld\n",
                        (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;
        }
}

/*
 *              Check the consistency of an attribute
 *
 *      Do the general consistency checks of the selected attribute :
 *      - the required fields can be accessed
 *      - the variable fields do not overflow
 *      - the attribute is [non-]resident if it must be
 *      - miscelleaneous checks
 *
 *      Returns 0 if the checks pass
 *              -1 with errno = EIO otherwise
 */

int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref)
{
        const FILE_NAME_ATTR *fn;
        const INDEX_ROOT *ir;
        u64 inum;
        int ret;

        /*
         * The attribute was found to fully lie within the MFT
         * record, now make sure its relevant parts (name, runlist,
         * value) also lie within. The first step is to make sure
         * the attribute has the minimum length so that accesses to
         * the lengths and offsets of these parts are safe.
         */
        ret = 0;
        inum = MREF(mref);
        if (a->non_resident) {
                if ((a->non_resident != 1)
                    || (le32_to_cpu(a->length)
                        < offsetof(ATTR_RECORD, non_resident_end))
                    || (le16_to_cpu(a->mapping_pairs_offset)
                                >= le32_to_cpu(a->length))
                    || (a->name_length
                         && (((u32)le16_to_cpu(a->name_offset)
                                + a->name_length * sizeof(ntfschar))
                                > le32_to_cpu(a->length)))
                    || (le64_to_cpu(a->highest_vcn)
                                < le64_to_cpu(a->lowest_vcn))) {
                        ntfs_log_error("Corrupt non resident attribute"
                                " 0x%x in MFT record %lld\n",
                                (int)le32_to_cpu(a->type),
                                (long long)inum);
                        errno = EIO;
                        ret = -1;
                }
        } else {
                if ((le32_to_cpu(a->length)
                        < offsetof(ATTR_RECORD, resident_end))
                    || (le32_to_cpu(a->value_length) & 0xff000000)
                    || (a->value_length
                        && ((le16_to_cpu(a->value_offset)
                                + le32_to_cpu(a->value_length))
                                > le32_to_cpu(a->length)))
                    || (a->name_length
                        && (((u32)le16_to_cpu(a->name_offset)
                                + a->name_length * sizeof(ntfschar))
                                > le32_to_cpu(a->length)))) {
                        ntfs_log_error("Corrupt resident attribute"
                                " 0x%x in MFT record %lld\n",
                                (int)le32_to_cpu(a->type),
                                (long long)inum);
                        errno = EIO;
                        ret = -1;
                }
        }
        if (!ret) {
                /*
                 * Checking whether an attribute must be [non-]resident
                 * is hard-coded for well-known ones. This should be
                 * done through ntfs_attr_can_be_non_resident(), based on
                 * $AttrDef, but this would give an easy way to bypass
                 * the checks.
                 * Attributes which are not well-known are not checked.
                 *
                 * Note : at this stage we know that a->length and
                 * a->value_length cannot look like being negative.
                 */
                switch(a->type) {
                case AT_FILE_NAME :
                        /* Check file names are resident and do not overflow */
                        fn = (const FILE_NAME_ATTR*)((const u8*)a
                                + le16_to_cpu(a->value_offset));
                        if (a->non_resident
                            || (le32_to_cpu(a->value_length)
                                < offsetof(FILE_NAME_ATTR, file_name))
                            || !fn->file_name_length
                            || ((fn->file_name_length * sizeof(ntfschar)
                                + offsetof(FILE_NAME_ATTR, file_name))
                                > le32_to_cpu(a->value_length))) {
                                ntfs_log_error("Corrupt file name"
                                        " attribute in MFT record %lld.\n",
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                case AT_INDEX_ROOT :
                        /* Check root index is resident and does not overflow */
                        ir = (const INDEX_ROOT*)((const u8*)a +
                                le16_to_cpu(a->value_offset));
                        /* index.allocated_size may overflow while resizing */
                        if (a->non_resident
                            || (le32_to_cpu(a->value_length)
                                < offsetof(INDEX_ROOT, index.reserved))
                            || (le32_to_cpu(ir->index.entries_offset)
                                < sizeof(INDEX_HEADER))
                            || (le32_to_cpu(ir->index.index_length)
                                < le32_to_cpu(ir->index.entries_offset))
                            || (le32_to_cpu(ir->index.allocated_size)
                                < le32_to_cpu(ir->index.index_length))
                            || (le32_to_cpu(a->value_length)
                                < (le32_to_cpu(ir->index.allocated_size)
                                    + offsetof(INDEX_ROOT, reserved)))) {
                                ntfs_log_error("Corrupt index root"
                                        " in MFT record %lld.\n",
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                case AT_STANDARD_INFORMATION :
                        if (a->non_resident
                            || (le32_to_cpu(a->value_length)
                                        < offsetof(STANDARD_INFORMATION,
                                                        v1_end))) {
                                ntfs_log_error("Corrupt standard information"
                                        " in MFT record %lld\n",
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                case AT_OBJECT_ID :
                        if (a->non_resident
                            || (le32_to_cpu(a->value_length)
                                        < sizeof(GUID))) {
                                ntfs_log_error("Corrupt object id"
                                        " in MFT record %lld\n",
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                case AT_VOLUME_NAME :
                case AT_EA_INFORMATION :
                        if (a->non_resident) {
                                ntfs_log_error("Attribute 0x%x in MFT record"
                                        " %lld should be resident.\n",
                                        (int)le32_to_cpu(a->type),
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                case AT_VOLUME_INFORMATION :
                        if (a->non_resident
                            || (le32_to_cpu(a->value_length)
                                        < sizeof(VOLUME_INFORMATION))) {
                                ntfs_log_error("Corrupt volume information"
                                        " in MFT record %lld\n",
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                case AT_INDEX_ALLOCATION :
                        if (!a->non_resident) {
                                ntfs_log_error("Corrupt index allocation"
                                        " in MFT record %lld",
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
                        }
                        break;
                default :
                        break;
                }
        }
        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", le32_to_cpu(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__, le32_to_cpu(type));
                return NULL;
        }
        for (ad = vol->attrdef; ((ptrdiff_t)((u8*)ad - (u8*)vol->attrdef
                                + sizeof(ATTR_DEF)) <= 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__, le32_to_cpu(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)", le32_to_cpu(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) le32_to_cpu(type), (unsigned) le16_to_cpu(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
                ? const_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) le32_to_cpu(type),
                        (long long) lowest_vcn, dataruns_size, (unsigned) le16_to_cpu(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", le32_to_cpu(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 = const_cpu_to_sle64(-1);
                a->allocated_size = const_cpu_to_sle64(0);
                a->data_size = const_cpu_to_sle64(0);
                a->initialized_size = const_cpu_to_sle64(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, le32_to_cpu(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, le32_to_cpu(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. */
                if (((u8 *)m + old_size) < ((u8 *)a + attr_size)) {
                        ntfs_log_error("Attribute 0x%x overflows"
                                " from MFT record\n",
                                (int)le32_to_cpu(a->type));
                        errno = EIO;
                        return (-1);
                }
                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);

        if (!a->value_length) {
                        /* Offset is unsafe when no value */
                int offset = ((offsetof(ATTR_RECORD, resident_end)
                        + a->name_length*sizeof(ntfschar) - 1) | 7) + 1;
                a->value_offset = cpu_to_le16(offset);
        }

        /* 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, le32_to_cpu(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 = const_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_sle64(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,
                        hole_type holes)
{
        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, le32_to_cpu(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) && (holes != HOLES_NONRES)) {
                /* 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 (holes == HOLES_NONRES) {
                        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);
        } 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, holes);
        }
        /* 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 (!NInoAttrList(na->ni) && ntfs_inode_add_attrlist(na->ni)) {
                        ntfs_log_perror("Could not add attribute list");
                        return -1;
                }
                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, holes);
        }

        /*
         * 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, holes);
        }
        /* 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, holes);

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, HOLES_OK);
        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, HOLES_NONRES);
        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, le32_to_cpu(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 = const_cpu_to_le16(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, le32_to_cpu(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, le32_to_cpu(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 = 0;
                        runlist_element *xrl;

                                /*
                                 * If attribute was not sparse, we only
                                 * have to check whether there is a hole
                                 * in the updated region.
                                 */
                        for (xrl = na->rl; xrl->length; xrl++) {
                                if (xrl->lcn < 0) {
                                        if (xrl->lcn == LCN_HOLE) {
                                                sparse = 1;
                                                break;
                                        }
                                        if (xrl->lcn != LCN_RL_NOT_MAPPED) {
                                                sparse = -1;
                                                break;
                                        }
                                }
                        }
                        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);
                        /* Updating sizes taints the extent holding the attr */
                        if (ctx->ntfs_ino)
                                NInoSetDirty(ctx->ntfs_ino);
                        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, with special case for mft itself */
                if (!na->ni->mft_no)
                        ni = ntfs_mft_rec_alloc(na->ni->vol,
                                na->type == AT_DATA);
                else
                        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:
        NAttrClearRunlistDirty(na);
        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, le32_to_cpu(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;
                }
                NAttrSetRunlistDirty(na);

                /* 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 (!(na->data_flags & ATTR_IS_ENCRYPTED)
                    && 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, le32_to_cpu(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;
                NAttrSetRunlistDirty(na);

                /* 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 {
                NAttrSetRunlistDirty(na);
                /* 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, le32_to_cpu(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) && !na->ni->vol->efs_raw) {
                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_i(na, newsize, holes);
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, inode %lld attr 0x%lx",
                                (long long)ni->mft_no,(long)le32_to_cpu(type));
                goto err_exit;
        }
                /*
                 * Consistency check : restrict to 65536 bytes.
                 *   index bitmaps may need more, but still limited by
                 *   the number of clusters.
                 */
        if (((u64)na->data_size > 65536)
            && ((type != AT_BITMAP)
                || ((u64)na->data_size > 
                        (u64)((ni->vol->nr_clusters + 7) >> 3)))) {
                ntfs_log_error("Corrupt attribute 0x%lx in inode %lld\n",
                                (long)le32_to_cpu(type),(long long)ni->mft_no);
                errno = EOVERFLOW;
                goto out;
        }
        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;
}

/*
 *              Shrink the size of a data attribute if needed
 *
 *      For non-resident attributes only.
 *      The space remains allocated.
 *
 *      Returns 0 if successful
 *              -1 if failed, with errno telling why
 */


int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name,
                int stream_name_len, off_t offset)
{
        ntfs_attr_search_ctx *ctx;
        ATTR_RECORD *a;
        int res;

        res = -1;
        ctx = ntfs_attr_get_search_ctx(ni, NULL);
        if (ctx) {
                if (!ntfs_attr_lookup(AT_DATA, stream_name, stream_name_len,
                        CASE_SENSITIVE, 0, NULL, 0, ctx)) {
                        a = ctx->attr;

                        if (a->non_resident
                            && (sle64_to_cpu(a->initialized_size) > offset)) {
                                a->initialized_size = cpu_to_sle64(offset);
                                a->data_size = a->initialized_size;
                        }
                        res = 0;
                }
                ntfs_attr_put_search_ctx(ctx);
        }
        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", le32_to_cpu(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", le32_to_cpu(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)];
                        /* FALLTHRU */
                        case 2:  nr_free += lut[*(buf + br - 2)];
                        /* FALLTHRU */
                        case 1:  nr_free += lut[*(buf + br - 1)];
                }
        }
        free(buf);
out:
        free(lut);
        if (!total || br < 0)
                return -1;
        return nr_free;
}