1N/A/**
1N/A * attrib.c - Attribute handling code. Part of the Linux-NTFS project.
1N/A *
1N/A * Copyright (c) 2000-2006 Anton Altaparmakov
1N/A * Copyright (c) 2002-2005 Richard Russon
1N/A * Copyright (c) 2002-2006 Szabolcs Szakacsits
1N/A * Copyright (c) 2004-2007 Yura Pakhuchiy
1N/A *
1N/A * This program/include file is free software; you can redistribute it and/or
1N/A * modify it under the terms of the GNU General Public License as published
1N/A * by the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program/include file is distributed in the hope that it will be
1N/A * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1N/A * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program (in the main directory of the Linux-NTFS
1N/A * distribution in the file COPYING); if not, write to the Free Software
1N/A * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1N/A */
1N/A
1N/A#ifdef HAVE_CONFIG_H
1N/A#include "config.h"
1N/A#endif
1N/A
1N/A#ifdef HAVE_STDIO_H
1N/A#include <stdio.h>
1N/A#endif
1N/A#ifdef HAVE_STRING_H
1N/A#include <string.h>
1N/A#endif
1N/A#ifdef HAVE_STDLIB_H
1N/A#include <stdlib.h>
1N/A#endif
1N/A#ifdef HAVE_ERRNO_H
1N/A#include <errno.h>
1N/A#endif
1N/A
1N/A#include "compat.h"
1N/A#include "attrib.h"
1N/A#include "attrlist.h"
1N/A#include "device.h"
1N/A#include "mft.h"
1N/A#include "debug.h"
1N/A#include "mst.h"
1N/A#include "volume.h"
1N/A#include "types.h"
1N/A#include "layout.h"
1N/A#include "inode.h"
1N/A#include "runlist.h"
1N/A#include "lcnalloc.h"
1N/A#include "dir.h"
1N/A#include "compress.h"
1N/A#include "bitmap.h"
1N/A#include "logging.h"
1N/A#include "support.h"
1N/A#include "crypto.h"
1N/A
1N/Antfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') };
1N/A
1N/A/**
1N/A * ntfs_get_attribute_value_length - Find the length of an attribute
1N/A * @a:
1N/A *
1N/A * Description...
1N/A *
1N/A * Returns:
1N/A */
1N/As64 ntfs_get_attribute_value_length(const ATTR_RECORD *a)
1N/A{
1N/A if (!a) {
1N/A errno = EINVAL;
1N/A return 0;
1N/A }
1N/A errno = 0;
1N/A if (a->non_resident)
1N/A return sle64_to_cpu(a->u.nonres.data_size);
1N/A return (s64)le32_to_cpu(a->u.res.value_length);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_get_attribute_value - Get a copy of an attribute
1N/A * @vol:
1N/A * @a:
1N/A * @b:
1N/A *
1N/A * Description...
1N/A *
1N/A * Returns:
1N/A */
1N/As64 ntfs_get_attribute_value(const ntfs_volume *vol,
1N/A const ATTR_RECORD *a, u8 *b)
1N/A{
1N/A runlist *rl;
1N/A s64 total, r;
1N/A int i;
1N/A
1N/A /* Sanity checks. */
1N/A if (!vol || !a || !b) {
1N/A errno = EINVAL;
1N/A return 0;
1N/A }
1N/A /* Complex attribute? */
1N/A /*
1N/A * Ignore the flags in case they are not zero for an attribute list
1N/A * attribute. Windows does not complain about invalid flags and chkdsk
1N/A * does not detect or fix them so we need to cope with it, too.
1N/A */
1N/A if (a->type != AT_ATTRIBUTE_LIST && a->flags) {
1N/A ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle "
1N/A "this yet.\n", le16_to_cpu(a->flags));
1N/A errno = EOPNOTSUPP;
1N/A return 0;
1N/A }
1N/A if (!a->non_resident) {
1N/A /* Attribute is resident. */
1N/A
1N/A /* Sanity check. */
1N/A if (le32_to_cpu(a->u.res.value_length) + le16_to_cpu(a->u.res.value_offset)
1N/A > le32_to_cpu(a->length)) {
1N/A return 0;
1N/A }
1N/A
1N/A memcpy(b, (const char*)a + le16_to_cpu(a->u.res.value_offset),
1N/A le32_to_cpu(a->u.res.value_length));
1N/A errno = 0;
1N/A return (s64)le32_to_cpu(a->u.res.value_length);
1N/A }
1N/A
1N/A /* Attribute is not resident. */
1N/A
1N/A /* If no data, return 0. */
1N/A if (!(a->u.nonres.data_size)) {
1N/A errno = 0;
1N/A return 0;
1N/A }
1N/A /*
1N/A * FIXME: What about attribute lists?!? (AIA)
1N/A */
1N/A /* Decompress the mapping pairs array into a runlist. */
1N/A rl = ntfs_mapping_pairs_decompress(vol, a, NULL);
1N/A if (!rl) {
1N/A errno = EINVAL;
1N/A return 0;
1N/A }
1N/A /*
1N/A * FIXED: We were overflowing here in a nasty fashion when we
1N/A * reach the last cluster in the runlist as the buffer will
1N/A * only be big enough to hold data_size bytes while we are
1N/A * reading in allocated_size bytes which is usually larger
1N/A * than data_size, since the actual data is unlikely to have a
1N/A * size equal to a multiple of the cluster size!
1N/A * FIXED2: We were also overflowing here in the same fashion
1N/A * when the data_size was more than one run smaller than the
1N/A * allocated size which happens with Windows XP sometimes.
1N/A */
1N/A /* Now load all clusters in the runlist into b. */
1N/A for (i = 0, total = 0; rl[i].length; i++) {
1N/A if (total + (rl[i].length << vol->cluster_size_bits) >=
1N/A sle64_to_cpu(a->u.nonres.data_size)) {
1N/A unsigned char *intbuf = NULL;
1N/A /*
1N/A * We have reached the last run so we were going to
1N/A * overflow when executing the ntfs_pread() which is
1N/A * BAAAAAAAD!
1N/A * Temporary fix:
1N/A * Allocate a new buffer with size:
1N/A * rl[i].length << vol->cluster_size_bits, do the
1N/A * read into our buffer, then memcpy the correct
1N/A * amount of data into the caller supplied buffer,
1N/A * free our buffer, and continue.
1N/A * We have reached the end of data size so we were
1N/A * going to overflow in the same fashion.
1N/A * Temporary fix: same as above.
1N/A */
1N/A intbuf = ntfs_malloc(rl[i].length <<
1N/A vol->cluster_size_bits);
1N/A if (!intbuf) {
1N/A int eo = errno;
1N/A free(rl);
1N/A errno = eo;
1N/A return 0;
1N/A }
1N/A /*
1N/A * FIXME: If compressed file: Only read if lcn != -1.
1N/A * Otherwise, we are dealing with a sparse run and we
1N/A * just memset the user buffer to 0 for the length of
1N/A * the run, which should be 16 (= compression unit
1N/A * size).
1N/A * FIXME: Really only when file is compressed, or can
1N/A * we have sparse runs in uncompressed files as well?
1N/A * - Yes we can, in sparse files! But not necessarily
1N/A * size of 16, just run length.
1N/A */
1N/A r = ntfs_pread(vol->u.dev, rl[i].lcn <<
1N/A vol->cluster_size_bits, rl[i].length <<
1N/A vol->cluster_size_bits, intbuf);
1N/A if (r != rl[i].length << vol->cluster_size_bits) {
1N/A#define ESTR "Error reading attribute value"
1N/A if (r == -1) {
1N/A int eo = errno;
1N/A ntfs_log_perror(ESTR);
1N/A errno = eo;
1N/A } else if (r < rl[i].length <<
1N/A vol->cluster_size_bits) {
1N/A ntfs_log_debug(ESTR": Ran out of "
1N/A "input data.\n");
1N/A errno = EIO;
1N/A } else {
1N/A ntfs_log_debug(ESTR": unknown error\n");
1N/A errno = EIO;
1N/A }
1N/A#undef ESTR
1N/A free(rl);
1N/A free(intbuf);
1N/A return 0;
1N/A }
1N/A memcpy(b + total, intbuf, sle64_to_cpu(a->u.nonres.data_size) -
1N/A total);
1N/A free(intbuf);
1N/A total = sle64_to_cpu(a->u.nonres.data_size);
1N/A break;
1N/A }
1N/A /*
1N/A * FIXME: If compressed file: Only read if lcn != -1.
1N/A * Otherwise, we are dealing with a sparse run and we just
1N/A * memset the user buffer to 0 for the length of the run, which
1N/A * should be 16 (= compression unit size).
1N/A * FIXME: Really only when file is compressed, or can
1N/A * we have sparse runs in uncompressed files as well?
1N/A * - Yes we can, in sparse files! But not necessarily size of
1N/A * 16, just run length.
1N/A */
1N/A r = ntfs_pread(vol->u.dev, rl[i].lcn << vol->cluster_size_bits,
1N/A rl[i].length << vol->cluster_size_bits,
1N/A b + total);
1N/A if (r != rl[i].length << vol->cluster_size_bits) {
1N/A#define ESTR "Error reading attribute value"
1N/A if (r == -1) {
1N/A int eo = errno;
1N/A ntfs_log_perror(ESTR);
1N/A errno = eo;
1N/A } else if (r < rl[i].length << vol->cluster_size_bits) {
1N/A ntfs_log_debug(ESTR ": Ran out of "
1N/A "input data.\n");
1N/A errno = EIO;
1N/A } else {
1N/A ntfs_log_debug(ESTR ": unknown error\n");
1N/A errno = EIO;
1N/A }
1N/A#undef ESTR
1N/A free(rl);
1N/A return 0;
1N/A }
1N/A total += r;
1N/A }
1N/A free(rl);
1N/A return total;
1N/A}
1N/A
1N/A/* Already cleaned up code below, but still look for FIXME:... */
1N/A
1N/A/**
1N/A * __ntfs_attr_init - primary initialization of an ntfs attribute structure
1N/A * @na: ntfs attribute to initialize
1N/A * @ni: ntfs inode with which to initialize the ntfs attribute
1N/A * @type: attribute type
1N/A * @name: attribute name in little endian Unicode or NULL
1N/A * @name_len: length of attribute @name in Unicode characters (if @name given)
1N/A *
1N/A * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len.
1N/A */
1N/Astatic void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
1N/A const ATTR_TYPES type, ntfschar *name, const u32 name_len)
1N/A{
1N/A na->rl = NULL;
1N/A na->ni = ni;
1N/A na->type = type;
1N/A na->name = name;
1N/A if (name)
1N/A na->name_len = name_len;
1N/A else
1N/A na->name_len = 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_init - initialize an ntfs_attr with data sizes and status
1N/A * @na:
1N/A * @non_resident:
1N/A * @compressed:
1N/A * @encrypted:
1N/A * @sparse:
1N/A * @allocated_size:
1N/A * @data_size:
1N/A * @initialized_size:
1N/A * @compressed_size:
1N/A * @compression_unit:
1N/A *
1N/A * Final initialization for an ntfs attribute.
1N/A */
1N/Avoid ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
1N/A const BOOL compressed, const BOOL encrypted, const BOOL sparse,
1N/A const s64 allocated_size, const s64 data_size,
1N/A const s64 initialized_size, const s64 compressed_size,
1N/A const u8 compression_unit)
1N/A{
1N/A if (!NAttrInitialized(na)) {
1N/A if (non_resident)
1N/A NAttrSetNonResident(na);
1N/A if (compressed)
1N/A NAttrSetCompressed(na);
1N/A if (encrypted)
1N/A NAttrSetEncrypted(na);
1N/A if (sparse)
1N/A NAttrSetSparse(na);
1N/A na->allocated_size = allocated_size;
1N/A na->data_size = data_size;
1N/A na->initialized_size = initialized_size;
1N/A if (compressed || sparse) {
1N/A ntfs_volume *vol = na->ni->vol;
1N/A
1N/A na->compressed_size = compressed_size;
1N/A na->compression_block_clusters = 1 << compression_unit;
1N/A na->compression_block_size = 1 << (compression_unit +
1N/A vol->cluster_size_bits);
1N/A na->compression_block_size_bits = ffs(
1N/A na->compression_block_size) - 1;
1N/A }
1N/A NAttrSetInitialized(na);
1N/A }
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_open - open an ntfs attribute for access
1N/A * @ni: open ntfs inode in which the ntfs attribute resides
1N/A * @type: attribute type
1N/A * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL
1N/A * @name_len: length of attribute @name in Unicode characters (if @name given)
1N/A *
1N/A * Allocate a new ntfs attribute structure, initialize it with @ni, @type,
1N/A * @name, and @name_len, then return it. Return NULL on error with
1N/A * errno set to the error code.
1N/A *
1N/A * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you
1N/A * do not care whether the attribute is named or not set @name to NULL. In
1N/A * both those cases @name_len is not used at all.
1N/A */
1N/Antfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
1N/A ntfschar *name, u32 name_len)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A ntfs_attr *na;
1N/A ATTR_RECORD *a;
1N/A struct list_head *pos;
1N/A int err;
1N/A BOOL cs;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
1N/A (unsigned long long)ni->mft_no, type);
1N/A if (!ni || !ni->vol || !ni->mrec) {
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A /* Check cache, maybe this attribute already opened? */
1N/A list_for_each(pos, &ni->attr_cache) {
1N/A ntfs_attr *tmp_na;
1N/A
1N/A tmp_na = list_entry(pos, ntfs_attr, list_entry);
1N/A if (tmp_na->type == type && tmp_na->name_len == name_len &&
1N/A !ntfs_ucsncmp(tmp_na->name, name, name_len)) {
1N/A ntfs_log_trace("Found this attribute in cache, "
1N/A "increment reference count and "
1N/A "return it.\n");
1N/A tmp_na->nr_references++;
1N/A return tmp_na;
1N/A }
1N/A }
1N/A /* Search failed. Properly open attrbute. */
1N/A na = calloc(sizeof(ntfs_attr), 1);
1N/A if (!na)
1N/A return NULL;
1N/A if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) {
1N/A name = ntfs_ucsndup(name, name_len);
1N/A if (!name) {
1N/A err = errno;
1N/A free(na);
1N/A errno = err;
1N/A return NULL;
1N/A }
1N/A }
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx) {
1N/A err = errno;
1N/A goto err_out;
1N/A }
1N/A if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) {
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A
1N/A a = ctx->attr;
1N/A /*
1N/A * Wipe the flags in case they are not zero for an attribute list
1N/A * attribute. Windows does not complain about invalid flags and chkdsk
1N/A * does not detect or fix them so we need to cope with it, too.
1N/A */
1N/A if (type == AT_ATTRIBUTE_LIST)
1N/A a->flags = 0;
1N/A cs = (a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? 1 : 0;
1N/A if (!name) {
1N/A if (a->name_length) {
1N/A name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu(
1N/A a->name_offset)), a->name_length);
1N/A if (!name) {
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A name_len = a->name_length;
1N/A } else {
1N/A name = AT_UNNAMED;
1N/A name_len = 0;
1N/A }
1N/A }
1N/A __ntfs_attr_init(na, ni, type, name, name_len);
1N/A if (a->non_resident) {
1N/A ntfs_attr_init(na, TRUE, (a->flags & ATTR_IS_COMPRESSED)? 1 : 0,
1N/A (a->flags & ATTR_IS_ENCRYPTED) ? 1 : 0,
1N/A (a->flags & ATTR_IS_SPARSE) ? 1 : 0,
1N/A sle64_to_cpu(a->u.nonres.allocated_size),
1N/A sle64_to_cpu(a->u.nonres.data_size),
1N/A sle64_to_cpu(a->u.nonres.initialized_size),
1N/A cs ? sle64_to_cpu(a->u.nonres.compressed_size) : 0,
1N/A cs ? a->u.nonres.compression_unit : 0);
1N/A } else {
1N/A s64 l = le32_to_cpu(a->u.res.value_length);
1N/A ntfs_attr_init(na, FALSE, (a->flags & ATTR_IS_COMPRESSED) ? 1:0,
1N/A (a->flags & ATTR_IS_ENCRYPTED) ? 1 : 0,
1N/A (a->flags & ATTR_IS_SPARSE) ? 1 : 0,
1N/A (l + 7) & ~7, l, l, cs ? (l + 7) & ~7 : 0, 0);
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (NAttrEncrypted(na))
1N/A ntfs_crypto_attr_open(na);
1N/A list_add_tail(&na->list_entry, &ni->attr_cache);
1N/A na->nr_references = 1;
1N/A return na;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/Aerr_out:
1N/A free(na);
1N/A errno = err;
1N/A return NULL;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_close - free an ntfs attribute structure
1N/A * @na: ntfs attribute structure to free
1N/A *
1N/A * Release all memory associated with the ntfs attribute @na and then release
1N/A * @na itself.
1N/A */
1N/Avoid ntfs_attr_close(ntfs_attr *na)
1N/A{
1N/A if (!na)
1N/A return;
1N/A na->nr_references--;
1N/A if (na->nr_references) {
1N/A ntfs_log_trace("There are %d more references left to "
1N/A "this attribute.\n", na->nr_references);
1N/A return;
1N/A }
1N/A ntfs_log_trace("There are no more references left to this attribute\n");
1N/A list_del(&na->list_entry);
1N/A if (NAttrEncrypted(na))
1N/A ntfs_crypto_attr_close(na);
1N/A if (NAttrNonResident(na) && na->rl)
1N/A free(na->rl);
1N/A /* Don't release if using an internal constant. */
1N/A if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30)
1N/A free(na->name);
1N/A free(na);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute
1N/A * @na: ntfs attribute for which to map (part of) a runlist
1N/A * @vcn: map runlist part containing this vcn
1N/A *
1N/A * Map the part of a runlist containing the @vcn of the ntfs attribute @na.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn)
1N/A{
1N/A LCN lcn;
1N/A ntfs_attr_search_ctx *ctx;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n",
1N/A (unsigned long long)na->ni->mft_no, na->type, (long long)vcn);
1N/A
1N/A lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
1N/A if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT)
1N/A return 0;
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A
1N/A /* Find the attribute in the mft record. */
1N/A if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
1N/A vcn, NULL, 0, ctx)) {
1N/A runlist_element *rl;
1N/A
1N/A /* Decode the runlist. */
1N/A rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr,
1N/A na->rl);
1N/A if (rl) {
1N/A na->rl = rl;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return 0;
1N/A }
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_map_runlist_range - map (a part of) a runlist of an ntfs attribute
1N/A * @na: ntfs attribute for which to map (part of) a runlist
1N/A * @from_vcn: map runlist part starting this vcn
1N/A * @to_vcn: map runlist part ending this vcn
1N/A *
1N/A * Map the part of a runlist from containing the @from_vcn to containing the
1N/A * @to_vcn of an ntfs attribute @na. It is OK for @to_vcn to be beyond last run.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_map_runlist_range(ntfs_attr *na, VCN from_vcn, VCN to_vcn)
1N/A{
1N/A ntfs_attr_search_ctx *ctx = NULL;
1N/A runlist *rl;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, "
1N/A "from_vcn 0x%llx, to_vcn 0x%llx.\n",
1N/A (unsigned long long)na->ni->mft_no, na->type,
1N/A (long long)from_vcn, (long long)to_vcn);
1N/A
1N/A /* Map extent with @from_vcn. */
1N/A if (ntfs_attr_map_runlist(na, from_vcn))
1N/A goto err_out;
1N/A
1N/A for (rl = na->rl; rl->vcn <= to_vcn;) {
1N/A /* Skip not interesting to us runs. */
1N/A if (rl->lcn >= 0 || rl->lcn == LCN_HOLE || (rl->vcn +
1N/A rl->length < from_vcn &&
1N/A rl->lcn == LCN_RL_NOT_MAPPED)) {
1N/A rl++;
1N/A continue;
1N/A }
1N/A
1N/A /* We reached the end of runlist, just exit. */
1N/A if (rl->lcn == LCN_ENOENT)
1N/A break;
1N/A
1N/A /* Check for errors. */
1N/A if (rl->lcn < 0 && rl->lcn != LCN_RL_NOT_MAPPED) {
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A
1N/A /* Runlist is not mapped here. */
1N/A if (!ctx) {
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A goto err_out;
1N/A }
1N/A /* Find the attribute in the mft record. */
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len,
1N/A CASE_SENSITIVE, rl->vcn, NULL, 0,
1N/A ctx))
1N/A goto err_out;
1N/A
1N/A /* Decode the runlist. */
1N/A rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr,
1N/A na->rl);
1N/A if (!rl)
1N/A goto err_out;
1N/A na->rl = rl;
1N/A }
1N/A
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ntfs_log_trace("Done.\n");
1N/A return 0;
1N/Aerr_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ntfs_log_trace("Failed.\n");
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute
1N/A * @na: ntfs attribute for which to map the runlist
1N/A *
1N/A * Map the whole runlist of the ntfs attribute @na. For an attribute made up
1N/A * of only one attribute extent this is the same as calling
1N/A * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this
1N/A * will map the runlist fragments from each of the extents thus giving access
1N/A * to the entirety of the disk allocation of an attribute.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_map_whole_runlist(ntfs_attr *na)
1N/A{
1N/A VCN next_vcn, last_vcn, highest_vcn;
1N/A ntfs_attr_search_ctx *ctx;
1N/A ntfs_volume *vol = na->ni->vol;
1N/A ATTR_RECORD *a;
1N/A int err;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
1N/A (unsigned long long)na->ni->mft_no, na->type);
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A
1N/A /* Map all attribute extents one by one. */
1N/A next_vcn = last_vcn = highest_vcn = 0;
1N/A a = NULL;
1N/A while (1) {
1N/A runlist_element *rl;
1N/A
1N/A int not_mapped = 0;
1N/A if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED)
1N/A not_mapped = 1;
1N/A
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len,
1N/A CASE_SENSITIVE, next_vcn, NULL, 0, ctx))
1N/A break;
1N/A
1N/A a = ctx->attr;
1N/A
1N/A if (not_mapped) {
1N/A /* Decode the runlist. */
1N/A rl = ntfs_mapping_pairs_decompress(na->ni->vol,
1N/A a, na->rl);
1N/A if (!rl)
1N/A goto err_out;
1N/A na->rl = rl;
1N/A }
1N/A
1N/A /* Are we in the first extent? */
1N/A if (!next_vcn) {
1N/A if (a->u.nonres.lowest_vcn) {
1N/A ntfs_log_trace("First extent of attribute has "
1N/A "non zero lowest_vcn. "
1N/A "Inode is corrupt.\n");
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A /* Get the last vcn in the attribute. */
1N/A last_vcn = sle64_to_cpu(a->u.nonres.allocated_size) >>
1N/A vol->cluster_size_bits;
1N/A }
1N/A
1N/A /* Get the lowest vcn for the next extent. */
1N/A highest_vcn = sle64_to_cpu(a->u.nonres.highest_vcn);
1N/A next_vcn = highest_vcn + 1;
1N/A
1N/A /* Only one extent or error, which we catch below. */
1N/A if (next_vcn <= 0) {
1N/A errno = ENOENT;
1N/A break;
1N/A }
1N/A
1N/A /* Avoid endless loops due to corruption. */
1N/A if (next_vcn < sle64_to_cpu(a->u.nonres.lowest_vcn)) {
1N/A ntfs_log_trace("Inode has corrupt attribute list "
1N/A "attribute.\n");
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A }
1N/A if (!a) {
1N/A if (errno == ENOENT)
1N/A ntfs_log_trace("Attribute not found. "
1N/A "Inode is corrupt.\n");
1N/A else
1N/A ntfs_log_trace("Inode is corrupt.\n");
1N/A goto err_out;
1N/A }
1N/A if (highest_vcn && highest_vcn != last_vcn - 1) {
1N/A ntfs_log_trace("Failed to load the complete run list for the "
1N/A "attribute. Bug or corrupt inode.\n");
1N/A ntfs_log_trace("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n",
1N/A (long long)highest_vcn,
1N/A (long long)last_vcn - 1);
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A err = errno;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (err == ENOENT)
1N/A return 0;
1N/Aout_now:
1N/A errno = err;
1N/A return -1;
1N/Aerr_out:
1N/A err = errno;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A goto out_now;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute
1N/A * @na: ntfs attribute whose runlist to use for conversion
1N/A * @vcn: vcn to convert
1N/A *
1N/A * Convert the virtual cluster number @vcn of an attribute into a logical
1N/A * cluster number (lcn) of a device using the runlist @na->rl to map vcns to
1N/A * their corresponding lcns.
1N/A *
1N/A * If the @vcn is not mapped yet, attempt to map the attribute extent
1N/A * containing the @vcn and retry the vcn to lcn conversion.
1N/A *
1N/A * Since lcns must be >= 0, we use negative return values with special meaning:
1N/A *
1N/A * Return value Meaning / Description
1N/A * ==========================================
1N/A * -1 = LCN_HOLE Hole / not allocated on disk.
1N/A * -3 = LCN_ENOENT There is no such vcn in the attribute.
1N/A * -4 = LCN_EINVAL Input parameter error.
1N/A * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory.
1N/A */
1N/ALCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn)
1N/A{
1N/A LCN lcn;
1N/A BOOL is_retry = FALSE;
1N/A
1N/A if (!na || !NAttrNonResident(na) || vcn < 0)
1N/A return (LCN)LCN_EINVAL;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
1N/A long)na->ni->mft_no, na->type);
1N/Aretry:
1N/A /* Convert vcn to lcn. If that fails map the runlist and retry once. */
1N/A lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
1N/A if (lcn >= 0)
1N/A return lcn;
1N/A if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) {
1N/A is_retry = TRUE;
1N/A goto retry;
1N/A }
1N/A /*
1N/A * If the attempt to map the runlist failed, or we are getting
1N/A * LCN_RL_NOT_MAPPED despite having mapped the attribute extent
1N/A * successfully, something is really badly wrong...
1N/A */
1N/A if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED)
1N/A return (LCN)LCN_EIO;
1N/A /* lcn contains the appropriate error code. */
1N/A return lcn;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute
1N/A * @na: ntfs attribute whose runlist to search
1N/A * @vcn: vcn to find
1N/A *
1N/A * Find the virtual cluster number @vcn in the runlist of the ntfs attribute
1N/A * @na and return the the address of the runlist element containing the @vcn.
1N/A *
1N/A * Note you need to distinguish between the lcn of the returned runlist
1N/A * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes
1N/A * on read and allocate clusters on write. You need to update the runlist, the
1N/A * attribute itself as well as write the modified mft record to disk.
1N/A *
1N/A * If there is an error return NULL with errno set to the error code. The
1N/A * following error codes are defined:
1N/A * EINVAL Input parameter error.
1N/A * ENOENT There is no such vcn in the runlist.
1N/A * ENOMEM Not enough memory.
1N/A * EIO I/O error or corrupt metadata.
1N/A */
1N/Arunlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn)
1N/A{
1N/A runlist_element *rl;
1N/A BOOL is_retry = FALSE;
1N/A
1N/A if (!na || !NAttrNonResident(na) || vcn < 0) {
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n",
1N/A (unsigned long long)na->ni->mft_no, na->type,
1N/A (long long)vcn);
1N/Aretry:
1N/A rl = na->rl;
1N/A if (!rl)
1N/A goto map_rl;
1N/A if (vcn < rl[0].vcn)
1N/A goto map_rl;
1N/A while (rl->length) {
1N/A if (vcn < rl[1].vcn) {
1N/A if (rl->lcn >= (LCN)LCN_HOLE)
1N/A return rl;
1N/A break;
1N/A }
1N/A rl++;
1N/A }
1N/A switch (rl->lcn) {
1N/A case (LCN)LCN_RL_NOT_MAPPED:
1N/A goto map_rl;
1N/A case (LCN)LCN_ENOENT:
1N/A errno = ENOENT;
1N/A break;
1N/A case (LCN)LCN_EINVAL:
1N/A errno = EINVAL;
1N/A break;
1N/A default:
1N/A errno = EIO;
1N/A break;
1N/A }
1N/A return NULL;
1N/Amap_rl:
1N/A /* The @vcn is in an unmapped region, map the runlist and retry. */
1N/A if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) {
1N/A is_retry = TRUE;
1N/A goto retry;
1N/A }
1N/A /*
1N/A * If we already retried or the mapping attempt failed something has
1N/A * gone badly wrong. EINVAL and ENOENT coming from a failed mapping
1N/A * attempt are equivalent to errors for us as they should not happen
1N/A * in our code paths.
1N/A */
1N/A if (is_retry || errno == EINVAL || errno == ENOENT)
1N/A errno = EIO;
1N/A return NULL;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure
1N/A * @na: ntfs attribute to read from
1N/A * @pos: byte position in the attribute to begin reading from
1N/A * @count: number of bytes to read
1N/A * @b: output data buffer
1N/A *
1N/A * This function will read @count bytes starting at offset @pos from the ntfs
1N/A * attribute @na into the data buffer @b.
1N/A *
1N/A * On success, return the number of successfully read bytes. If this number is
1N/A * lower than @count this means that the read reached end of file or that an
1N/A * error was encountered during the read so that the read is partial. 0 means
1N/A * end of file or nothing was read (also return 0 when @count is 0).
1N/A *
1N/A * On error and nothing has been read, return -1 with errno set appropriately
1N/A * to the return code of ntfs_pread(), or to EINVAL in case of invalid
1N/A * arguments.
1N/A */
1N/As64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b)
1N/A{
1N/A s64 br, to_read, ofs, total, total2;
1N/A ntfs_volume *vol;
1N/A runlist_element *rl;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, "
1N/A "count 0x%llx.\n", (unsigned long long)na->ni->mft_no,
1N/A na->type, (long long)pos, (long long)count);
1N/A if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A /*
1N/A * If this is a compressed attribute it needs special treatment, but
1N/A * only if it is non-resident.
1N/A */
1N/A if (NAttrCompressed(na) && NAttrNonResident(na))
1N/A return ntfs_compressed_attr_pread(na, pos, count, b);
1N/A /*
1N/A * Encrypted non-resident attributes are not supported. We return
1N/A * access denied, which is what Windows NT4 does, too.
1N/A */
1N/A if (NAttrEncrypted(na) && NAttrNonResident(na))
1N/A return ntfs_crypto_attr_pread(na, pos, count, b);
1N/A
1N/A vol = na->ni->vol;
1N/A if (!count)
1N/A return 0;
1N/A /* Truncate reads beyond end of attribute. */
1N/A if (pos + count > na->data_size) {
1N/A if (pos >= na->data_size)
1N/A return 0;
1N/A count = na->data_size - pos;
1N/A }
1N/A /* If it is a resident attribute, get the value from the mft record. */
1N/A if (!NAttrNonResident(na)) {
1N/A ntfs_attr_search_ctx *ctx;
1N/A char *val;
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
1N/A 0, NULL, 0, ctx)) {
1N/A int eo;
1N/Ares_err_out:
1N/A eo = errno;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = eo;
1N/A return -1;
1N/A }
1N/A val = (char*)ctx->attr + le16_to_cpu(ctx->attr->u.res.value_offset);
1N/A if (val < (char*)ctx->attr || val +
1N/A le32_to_cpu(ctx->attr->u.res.value_length) >
1N/A (char*)ctx->mrec + vol->mft_record_size) {
1N/A errno = EIO;
1N/A goto res_err_out;
1N/A }
1N/A memcpy(b, val + pos, count);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return count;
1N/A }
1N/A total = total2 = 0;
1N/A /* Zero out reads beyond initialized size. */
1N/A if (pos + count > na->initialized_size) {
1N/A if (pos >= na->initialized_size) {
1N/A memset(b, 0, count);
1N/A return count;
1N/A }
1N/A total2 = pos + count - na->initialized_size;
1N/A count -= total2;
1N/A memset((u8*)b + count, 0, total2);
1N/A }
1N/A /* Find the runlist element containing the vcn. */
1N/A rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
1N/A if (!rl) {
1N/A /*
1N/A * If the vcn is not present it is an out of bounds read.
1N/A * However, we already truncated the read to the data_size,
1N/A * so getting this here is an error.
1N/A */
1N/A if (errno == ENOENT)
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A /*
1N/A * Gather the requested data into the linear destination buffer. Note,
1N/A * a partial final vcn is taken care of by the @count capping of read
1N/A * length.
1N/A */
1N/A ofs = pos - (rl->vcn << vol->cluster_size_bits);
1N/A for (; count; rl++, ofs = 0) {
1N/A if (rl->lcn == LCN_RL_NOT_MAPPED) {
1N/A rl = ntfs_attr_find_vcn(na, rl->vcn);
1N/A if (!rl) {
1N/A if (errno == ENOENT)
1N/A errno = EIO;
1N/A goto rl_err_out;
1N/A }
1N/A /* Needed for case when runs merged. */
1N/A ofs = pos + total - (rl->vcn << vol->cluster_size_bits);
1N/A }
1N/A if (!rl->length)
1N/A goto rl_err_out;
1N/A if (rl->lcn < (LCN)0) {
1N/A if (rl->lcn != (LCN)LCN_HOLE)
1N/A goto rl_err_out;
1N/A /* It is a hole, just zero the matching @b range. */
1N/A to_read = min(count, (rl->length <<
1N/A vol->cluster_size_bits) - ofs);
1N/A memset(b, 0, to_read);
1N/A /* Update progress counters. */
1N/A total += to_read;
1N/A count -= to_read;
1N/A b = (u8*)b + to_read;
1N/A continue;
1N/A }
1N/A /* It is a real lcn, read it into @dst. */
1N/A to_read = min(count, (rl->length << vol->cluster_size_bits) -
1N/A ofs);
1N/Aretry:
1N/A ntfs_log_trace("Reading 0x%llx bytes from vcn 0x%llx, "
1N/A "lcn 0x%llx, ofs 0x%llx.\n", to_read, rl->vcn,
1N/A rl->lcn, ofs);
1N/A br = ntfs_pread(vol->u.dev, (rl->lcn << vol->cluster_size_bits) +
1N/A ofs, to_read, b);
1N/A /* If everything ok, update progress counters and continue. */
1N/A if (br > 0) {
1N/A total += br;
1N/A count -= br;
1N/A b = (u8*)b + br;
1N/A continue;
1N/A }
1N/A /* If the syscall was interrupted, try again. */
1N/A if (br == (s64)-1 && errno == EINTR)
1N/A goto retry;
1N/A if (total)
1N/A return total;
1N/A if (!br)
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A /* Finally, return the number of bytes read. */
1N/A return total + total2;
1N/Arl_err_out:
1N/A if (total)
1N/A return total;
1N/A errno = EIO;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_pwrite - positioned write to an ntfs attribute
1N/A * @na: ntfs attribute to write to
1N/A * @pos: position in the attribute to write to
1N/A * @count: number of bytes to write
1N/A * @b: data buffer to write to disk
1N/A *
1N/A * This function will write @count bytes from data buffer @b to ntfs attribute
1N/A * @na at position @pos.
1N/A *
1N/A * On success, return the number of successfully written bytes. If this number
1N/A * is lower than @count this means that an error was encountered during the
1N/A * write so that the write is partial. 0 means nothing was written (also return
1N/A * 0 when @count is 0).
1N/A *
1N/A * On error and nothing has been written, return -1 with errno set
1N/A * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of
1N/A * invalid arguments.
1N/A */
1N/As64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
1N/A{
1N/A s64 written, to_write, ofs, total, old_initialized_size, old_data_size;
1N/A VCN update_from = -1;
1N/A ntfs_volume *vol;
1N/A ntfs_attr_search_ctx *ctx = NULL;
1N/A runlist_element *rl;
1N/A int eo;
1N/A struct {
1N/A unsigned int undo_initialized_size : 1;
1N/A unsigned int undo_data_size : 1;
1N/A unsigned int update_mapping_pairs : 1;
1N/A } need_to = { 0, 0, 0 };
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, "
1N/A "count 0x%llx.\n", na->ni->mft_no, na->type,
1N/A (long long)pos, (long long)count);
1N/A if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A vol = na->ni->vol;
1N/A /*
1N/A * Encrypted non-resident attributes are not supported. We return
1N/A * access denied, which is what Windows NT4 does, too.
1N/A */
1N/A if (NAttrEncrypted(na) && NAttrNonResident(na)) {
1N/A errno = EACCES;
1N/A return -1;
1N/A }
1N/A /* If this is a compressed attribute it needs special treatment. */
1N/A if (NAttrCompressed(na)) {
1N/A // TODO: Implement writing compressed attributes! (AIA)
1N/A // return ntfs_attr_pwrite_compressed(ntfs_attr *na,
1N/A // const s64 pos, s64 count, void *b);
1N/A errno = EOPNOTSUPP;
1N/A return -1;
1N/A }
1N/A if (!count)
1N/A return 0;
1N/A /* If the write reaches beyond the end, extend the attribute. */
1N/A old_data_size = na->data_size;
1N/A if (pos + count > na->data_size) {
1N/A if (__ntfs_attr_truncate(na, pos + count, FALSE)) {
1N/A eo = errno;
1N/A ntfs_log_trace("Attribute extend failed.\n");
1N/A errno = eo;
1N/A return -1;
1N/A }
1N/A need_to.undo_data_size = 1;
1N/A }
1N/A old_initialized_size = na->initialized_size;
1N/A /* If it is a resident attribute, write the data to the mft record. */
1N/A if (!NAttrNonResident(na)) {
1N/A char *val;
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A goto err_out;
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
1N/A 0, NULL, 0, ctx))
1N/A goto err_out;
1N/A val = (char*)ctx->attr + le16_to_cpu(ctx->attr->u.res.value_offset);
1N/A if (val < (char*)ctx->attr || val +
1N/A le32_to_cpu(ctx->attr->u.res.value_length) >
1N/A (char*)ctx->mrec + vol->mft_record_size) {
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A memcpy(val + pos, b, count);
1N/A if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
1N/A ctx->mrec)) {
1N/A /*
1N/A * NOTE: We are in a bad state at this moment. We have
1N/A * dirtied the mft record but we failed to commit it to
1N/A * disk. Since we have read the mft record ok before,
1N/A * it is unlikely to fail writing it, so is ok to just
1N/A * return error here... (AIA)
1N/A */
1N/A goto err_out;
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return count;
1N/A }
1N/A total = 0;
1N/A /* Handle writes beyond initialized_size. */
1N/A if (pos + count > na->initialized_size) {
1N/A /*
1N/A * Map runlist between initialized size and place we start
1N/A * writing at.
1N/A */
1N/A if (ntfs_attr_map_runlist_range(na, na->initialized_size >>
1N/A vol->cluster_size_bits,
1N/A pos >> vol->cluster_size_bits))
1N/A goto err_out;
1N/A /* Set initialized_size to @pos + @count. */
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A goto err_out;
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
1N/A 0, NULL, 0, ctx))
1N/A goto err_out;
1N/A /* If write starts beyond initialized_size, zero the gap. */
1N/A if (pos > na->initialized_size && ntfs_rl_fill_zero(vol,
1N/A na->rl, na->initialized_size,
1N/A pos - na->initialized_size))
1N/A goto err_out;
1N/A
1N/A ctx->attr->u.nonres.initialized_size = cpu_to_sle64(pos + count);
1N/A if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
1N/A ctx->mrec)) {
1N/A /*
1N/A * Undo the change in the in-memory copy and send it
1N/A * back for writing.
1N/A */
1N/A ctx->attr->u.nonres.initialized_size =
1N/A cpu_to_sle64(old_initialized_size);
1N/A ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
1N/A ctx->mrec);
1N/A goto err_out;
1N/A }
1N/A na->initialized_size = pos + count;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ctx = NULL;
1N/A /*
1N/A * NOTE: At this point the initialized_size in the mft record
1N/A * has been updated BUT there is random data on disk thus if
1N/A * we decide to abort, we MUST change the initialized_size
1N/A * again.
1N/A */
1N/A need_to.undo_initialized_size = 1;
1N/A }
1N/A /* Find the runlist element containing the vcn. */
1N/A rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
1N/A if (!rl) {
1N/A /*
1N/A * If the vcn is not present it is an out of bounds write.
1N/A * However, we already extended the size of the attribute,
1N/A * so getting this here must be an error of some kind.
1N/A */
1N/A if (errno == ENOENT)
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A /*
1N/A * Scatter the data from the linear data buffer to the volume. Note, a
1N/A * partial final vcn is taken care of by the @count capping of write
1N/A * length.
1N/A */
1N/A ofs = pos - (rl->vcn << vol->cluster_size_bits);
1N/A for (; count; rl++, ofs = 0) {
1N/A if (rl->lcn == LCN_RL_NOT_MAPPED) {
1N/A rl = ntfs_attr_find_vcn(na, rl->vcn);
1N/A if (!rl) {
1N/A if (errno == ENOENT)
1N/A errno = EIO;
1N/A goto rl_err_out;
1N/A }
1N/A /* Needed for case when runs merged. */
1N/A ofs = pos + total - (rl->vcn << vol->cluster_size_bits);
1N/A }
1N/A if (!rl->length) {
1N/A errno = EIO;
1N/A goto rl_err_out;
1N/A }
1N/A if (rl->lcn < (LCN)0) {
1N/A LCN lcn_seek_from = -1;
1N/A runlist *rlc;
1N/A VCN cur_vcn, from_vcn;
1N/A
1N/A if (rl->lcn != (LCN)LCN_HOLE) {
1N/A errno = EIO;
1N/A goto rl_err_out;
1N/A }
1N/A
1N/A to_write = min(count, (rl->length <<
1N/A vol->cluster_size_bits) - ofs);
1N/A
1N/A /* Instantiate the hole. */
1N/A cur_vcn = rl->vcn;
1N/A from_vcn = rl->vcn + (ofs >> vol->cluster_size_bits);
1N/A ntfs_log_trace("Instantiate hole with vcn 0x%llx.\n",
1N/A cur_vcn);
1N/A /*
1N/A * Map whole runlist to be able update mapping pairs
1N/A * later.
1N/A */
1N/A if (ntfs_attr_map_whole_runlist(na))
1N/A goto err_out;
1N/A /*
1N/A * Restore @rl, it probably get lost during runlist
1N/A * mapping.
1N/A */
1N/A rl = ntfs_attr_find_vcn(na, cur_vcn);
1N/A if (!rl) {
1N/A ntfs_log_error("BUG! Failed to find run after "
1N/A "mapping whole runlist. Please "
1N/A "report to the %s.\n",
1N/A NTFS_DEV_LIST);
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A /*
1N/A * Search backwards to find the best lcn to start
1N/A * seek from.
1N/A */
1N/A rlc = rl;
1N/A while (rlc->vcn) {
1N/A rlc--;
1N/A if (rlc->lcn >= 0) {
1N/A lcn_seek_from = rlc->lcn +
1N/A (from_vcn - rlc->vcn);
1N/A break;
1N/A }
1N/A }
1N/A if (lcn_seek_from == -1) {
1N/A /* Backwards search failed, search forwards. */
1N/A rlc = rl;
1N/A while (rlc->length) {
1N/A rlc++;
1N/A if (rlc->lcn >= 0) {
1N/A lcn_seek_from = rlc->lcn -
1N/A (rlc->vcn - from_vcn);
1N/A if (lcn_seek_from < -1)
1N/A lcn_seek_from = -1;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A /* Allocate clusters to instantiate the hole. */
1N/A rlc = ntfs_cluster_alloc(vol, from_vcn,
1N/A ((ofs + to_write - 1) >>
1N/A vol->cluster_size_bits) + 1 +
1N/A rl->vcn - from_vcn,
1N/A lcn_seek_from, DATA_ZONE);
1N/A if (!rlc) {
1N/A eo = errno;
1N/A ntfs_log_trace("Failed to allocate clusters "
1N/A "for hole instantiating.\n");
1N/A errno = eo;
1N/A goto err_out;
1N/A }
1N/A /* Merge runlists. */
1N/A rl = ntfs_runlists_merge(na->rl, rlc);
1N/A if (!rl) {
1N/A eo = errno;
1N/A ntfs_log_trace("Failed to merge runlists.\n");
1N/A if (ntfs_cluster_free_from_rl(vol, rlc)) {
1N/A ntfs_log_trace("Failed to free just "
1N/A "allocated clusters. Leaving "
1N/A "inconsistent metadata. "
1N/A "Run chkdsk\n");
1N/A }
1N/A errno = eo;
1N/A goto err_out;
1N/A }
1N/A na->rl = rl;
1N/A need_to.update_mapping_pairs = 1;
1N/A if (update_from == -1)
1N/A update_from = from_vcn;
1N/A rl = ntfs_attr_find_vcn(na, cur_vcn);
1N/A if (!rl) {
1N/A /*
1N/A * It's definitely a BUG, if we failed to find
1N/A * @cur_vcn, because we missed it during
1N/A * instantiating of the hole.
1N/A */
1N/A ntfs_log_error("BUG! Failed to find run after "
1N/A "instantiating. Please report "
1N/A "to the %s.\n", NTFS_DEV_LIST);
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A /* If leaved part of the hole go to the next run. */
1N/A if (rl->lcn < 0)
1N/A rl++;
1N/A /* Now LCN shouldn't be less than 0. */
1N/A if (rl->lcn < 0) {
1N/A ntfs_log_error("BUG! LCN is lesser than 0. "
1N/A "Please report to the %s.\n",
1N/A NTFS_DEV_LIST);
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A if (rl->vcn < cur_vcn) {
1N/A /*
1N/A * Clusters that replaced hole are merged with
1N/A * previous run, so we need to update offset.
1N/A */
1N/A ofs += (cur_vcn - rl->vcn) <<
1N/A vol->cluster_size_bits;
1N/A }
1N/A if (rl->vcn > cur_vcn) {
1N/A /*
1N/A * We left part of the hole, so update we need
1N/A * to update offset
1N/A */
1N/A ofs -= (rl->vcn - cur_vcn) <<
1N/A vol->cluster_size_bits;
1N/A }
1N/A /*
1N/A * Clear region between start of @rl->vcn cluster and
1N/A * @ofs if necessary.
1N/A */
1N/A if (ofs && ntfs_rl_fill_zero(vol, na->rl, rl->vcn <<
1N/A vol->cluster_size_bits, ofs))
1N/A goto err_out;
1N/A }
1N/A /* It is a real lcn, write it to the volume. */
1N/A to_write = min(count, (rl->length << vol->cluster_size_bits) -
1N/A ofs);
1N/Aretry:
1N/A ntfs_log_trace("Writing 0x%llx bytes to vcn 0x%llx, lcn 0x%llx,"
1N/A " ofs 0x%llx.\n", to_write, rl->vcn, rl->lcn,
1N/A ofs);
1N/A if (!NVolReadOnly(vol)) {
1N/A s64 pos = (rl->lcn << vol->cluster_size_bits) + ofs;
1N/A int bsize = 4096; /* FIXME: Test whether we need
1N/A PAGE_SIZE here. Eg., on IA64. */
1N/A /*
1N/A * Write 4096 size blocks if it's possible. This will
1N/A * cause the kernel not to seek and read disk blocks for
1N/A * filling the end of the buffer which increases write
1N/A * speed.
1N/A */
1N/A if (vol->cluster_size >= bsize && !(ofs % bsize) &&
1N/A (to_write % bsize) && ofs + to_write ==
1N/A na->initialized_size) {
1N/A char *cb;
1N/A s64 rounded = (to_write + bsize - 1) &
1N/A ~(bsize - 1);
1N/A
1N/A cb = ntfs_malloc(rounded);
1N/A if (!cb)
1N/A goto err_out;
1N/A memcpy(cb, b, to_write);
1N/A memset(cb + to_write, 0, rounded - to_write);
1N/A written = ntfs_pwrite(vol->u.dev, pos, rounded,
1N/A cb);
1N/A if (written > to_write)
1N/A written = to_write;
1N/A free(cb);
1N/A } else
1N/A written = ntfs_pwrite(vol->u.dev, pos, to_write,
1N/A b);
1N/A } else
1N/A written = to_write;
1N/A /* If everything ok, update progress counters and continue. */
1N/A if (written > 0) {
1N/A total += written;
1N/A count -= written;
1N/A b = (const u8*)b + written;
1N/A continue;
1N/A }
1N/A /* If the syscall was interrupted, try again. */
1N/A if (written == (s64)-1 && errno == EINTR)
1N/A goto retry;
1N/A if (!written)
1N/A errno = EIO;
1N/A goto rl_err_out;
1N/A }
1N/Adone:
1N/A if (ctx)
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A /* Update mapping pairs if needed. */
1N/A if (need_to.update_mapping_pairs) {
1N/A if (ntfs_attr_update_mapping_pairs(na, update_from)) {
1N/A /* FIXME: We want rollback here. */
1N/A ntfs_log_perror("%s(): Failed to update mapping pairs. "
1N/A "Leaving inconsistent metadata. "
1N/A "Run chkdsk!", "ntfs_attr_pwrite");
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A }
1N/A /* Finally, return the number of bytes written. */
1N/A return total;
1N/Arl_err_out:
1N/A eo = errno;
1N/A if (total) {
1N/A if (need_to.undo_initialized_size) {
1N/A if (pos + total > na->initialized_size)
1N/A goto done;
1N/A /*
1N/A * TODO: Need to try to change initialized_size. If it
1N/A * succeeds goto done, otherwise goto err_out. (AIA)
1N/A */
1N/A errno = EOPNOTSUPP;
1N/A goto err_out;
1N/A }
1N/A goto done;
1N/A }
1N/A errno = eo;
1N/Aerr_out:
1N/A eo = errno;
1N/A if (need_to.undo_initialized_size) {
1N/A int err;
1N/A
1N/A err = 0;
1N/A if (!ctx) {
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A err = 1;
1N/A } else
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A if (ctx) {
1N/A err = ntfs_attr_lookup(na->type, na->name,
1N/A na->name_len, 0, 0, NULL, 0, ctx);
1N/A if (!err) {
1N/A na->initialized_size = old_initialized_size;
1N/A ctx->attr->u.nonres.initialized_size = cpu_to_sle64(
1N/A old_initialized_size);
1N/A err = ntfs_mft_record_write(vol,
1N/A ctx->ntfs_ino->mft_no,
1N/A ctx->mrec);
1N/A }
1N/A }
1N/A if (err) {
1N/A /*
1N/A * FIXME: At this stage could try to recover by filling
1N/A * old_initialized_size -> new_initialized_size with
1N/A * data or at least zeroes. (AIA)
1N/A */
1N/A ntfs_log_error("Eeek! Failed to recover from error. "
1N/A "Leaving metadata in inconsistent "
1N/A "state! Run chkdsk!\n");
1N/A }
1N/A }
1N/A if (ctx)
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A /* Update mapping pairs if needed. */
1N/A if (need_to.update_mapping_pairs)
1N/A ntfs_attr_update_mapping_pairs(na, update_from);
1N/A /* Restore original data_size if needed. */
1N/A if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size))
1N/A ntfs_log_trace("Failed to restore data_size.\n");
1N/A errno = eo;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read
1N/A * @na: multi sector transfer protected ntfs attribute to read from
1N/A * @pos: byte position in the attribute to begin reading from
1N/A * @bk_cnt: number of mst protected blocks to read
1N/A * @bk_size: size of each mst protected block in bytes
1N/A * @dst: output data buffer
1N/A *
1N/A * This function will read @bk_cnt blocks of size @bk_size bytes each starting
1N/A * at offset @pos from the ntfs attribute @na into the data buffer @b.
1N/A *
1N/A * On success, the multi sector transfer fixups are applied and the number of
1N/A * read blocks is returned. If this number is lower than @bk_cnt this means
1N/A * that the read has either reached end of attribute or that an error was
1N/A * encountered during the read so that the read is partial. 0 means end of
1N/A * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0).
1N/A *
1N/A * On error and nothing has been read, return -1 with errno set appropriately
1N/A * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid
1N/A * arguments.
1N/A *
1N/A * NOTE: If an incomplete multi sector transfer is detected the magic is
1N/A * changed to BAAD but no error is returned, i.e. it is possible that any of
1N/A * the returned blocks have multi sector transfer errors. This should be
1N/A * detected by the caller by checking each block with is_baad_recordp(&block).
1N/A * The reasoning is that we want to fixup as many blocks as possible and we
1N/A * want to return even bad ones to the caller so, e.g. in case of ntfsck, the
1N/A * errors can be repaired.
1N/A */
1N/As64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt,
1N/A const u32 bk_size, void *dst)
1N/A{
1N/A s64 br;
1N/A u8 *end;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, "
1N/A "pos 0x%llx.\n", (unsigned long long)na->ni->mft_no,
1N/A na->type, (long long)pos);
1N/A if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst);
1N/A if (br <= 0)
1N/A return br;
1N/A br /= bk_size;
1N/A for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst +
1N/A bk_size)
1N/A ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size);
1N/A /* Finally, return the number of blocks read. */
1N/A return br;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write
1N/A * @na: multi sector transfer protected ntfs attribute to write to
1N/A * @pos: position in the attribute to write to
1N/A * @bk_cnt: number of mst protected blocks to write
1N/A * @bk_size: size of each mst protected block in bytes
1N/A * @src: data buffer to write to disk
1N/A *
1N/A * This function will write @bk_cnt blocks of size @bk_size bytes each from
1N/A * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na
1N/A * at position @pos.
1N/A *
1N/A * On success, return the number of successfully written blocks. If this number
1N/A * is lower than @bk_cnt this means that an error was encountered during the
1N/A * write so that the write is partial. 0 means nothing was written (also
1N/A * return 0 when @bk_cnt or @bk_size are 0).
1N/A *
1N/A * On error and nothing has been written, return -1 with errno set
1N/A * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case
1N/A * of invalid arguments.
1N/A *
1N/A * NOTE: We mst protect the data, write it, then mst deprotect it using a quick
1N/A * deprotect algorithm (no checking). This saves us from making a copy before
1N/A * the write and at the same time causes the usn to be incremented in the
1N/A * buffer. This conceptually fits in better with the idea that cached data is
1N/A * always deprotected and protection is performed when the data is actually
1N/A * going to hit the disk and the cache is immediately deprotected again
1N/A * simulating an mst read on the written data. This way cache coherency is
1N/A * achieved.
1N/A */
1N/As64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt,
1N/A const u32 bk_size, void *src)
1N/A{
1N/A s64 written, i;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, "
1N/A "pos 0x%llx.\n", (unsigned long long)na->ni->mft_no,
1N/A na->type, (long long)pos);
1N/A if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A if (!bk_cnt)
1N/A return 0;
1N/A /* Prepare data for writing. */
1N/A for (i = 0; i < bk_cnt; ++i) {
1N/A int err;
1N/A
1N/A err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
1N/A ((u8*)src + i * bk_size), bk_size);
1N/A if (err < 0) {
1N/A /* Abort write at this position. */
1N/A if (!i)
1N/A return err;
1N/A bk_cnt = i;
1N/A break;
1N/A }
1N/A }
1N/A /* Write the prepared data. */
1N/A written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src);
1N/A /* Quickly deprotect the data again. */
1N/A for (i = 0; i < bk_cnt; ++i)
1N/A ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i *
1N/A bk_size));
1N/A if (written <= 0)
1N/A return written;
1N/A /* Finally, return the number of complete blocks written. */
1N/A return written / bk_size;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_find - find (next) attribute in mft record
1N/A * @type: attribute type to find
1N/A * @name: attribute name to find (optional, i.e. NULL means don't care)
1N/A * @name_len: attribute name length (only needed if @name present)
1N/A * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
1N/A * @val: attribute value to find (optional, resident attributes only)
1N/A * @val_len: attribute value length
1N/A * @ctx: search context with mft record and attribute to search from
1N/A *
1N/A * You shouldn't need to call this function directly. Use lookup_attr() instead.
1N/A *
1N/A * ntfs_attr_find() takes a search context @ctx as parameter and searches the
1N/A * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an
1N/A * attribute of @type, optionally @name and @val. If found, ntfs_attr_find()
1N/A * returns 0 and @ctx->attr will point to the found attribute.
1N/A *
1N/A * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and
1N/A * @ctx->attr will point to the attribute before which the attribute being
1N/A * searched for would need to be inserted if such an action were to be desired.
1N/A *
1N/A * On actual error, ntfs_attr_find() returns -1 with errno set to the error
1N/A * code but not to ENOENT. In this case @ctx->attr is undefined and in
1N/A * particular do not rely on it not changing.
1N/A *
1N/A * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it
1N/A * is FALSE, the search begins after @ctx->attr.
1N/A *
1N/A * If @type is AT_UNUSED, return the first found attribute, i.e. one can
1N/A * enumerate all attributes by setting @type to AT_UNUSED and then calling
1N/A * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to
1N/A * indicate that there are no more entries. During the enumeration, each
1N/A * successful call of ntfs_attr_find() will return the next attribute in the
1N/A * mft record @ctx->mrec.
1N/A *
1N/A * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT.
1N/A * AT_END is not a valid attribute, its length is zero for example, thus it is
1N/A * safer to return error instead of success in this case. This also allows us
1N/A * to interoperate cleanly with ntfs_external_attr_find().
1N/A *
1N/A * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
1N/A * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
1N/A * match both named and unnamed attributes.
1N/A *
1N/A * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and
1N/A * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record
1N/A * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at
1N/A * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case
1N/A * sensitive. When @name is present, @name_len is the @name length in Unicode
1N/A * characters.
1N/A *
1N/A * If @name is not present (NULL), we assume that the unnamed attribute is
1N/A * being searched for.
1N/A *
1N/A * Finally, the resident attribute value @val is looked for, if present.
1N/A * If @val is not present (NULL), @val_len is ignored.
1N/A *
1N/A * ntfs_attr_find() only searches the specified mft record and it ignores the
1N/A * presence of an attribute list attribute (unless it is the one being searched
1N/A * for, obviously). If you need to take attribute lists into consideration, use
1N/A * ntfs_attr_lookup() instead (see below). This also means that you cannot use
1N/A * ntfs_attr_find() to search for extent records of non-resident attributes, as
1N/A * extents with lowest_vcn != 0 are usually described by the attribute list
1N/A * attribute only. - Note that it is possible that the first extent is only in
1N/A * the attribute list while the last extent is in the base mft record, so don't
1N/A * rely on being able to find the first extent in the base mft record.
1N/A *
1N/A * Warning: Never use @val when looking for attribute types which can be
1N/A * non-resident as this most likely will result in a crash!
1N/A */
1N/Astatic int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
1N/A const u32 name_len, const IGNORE_CASE_BOOL ic,
1N/A const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx)
1N/A{
1N/A ATTR_RECORD *a;
1N/A ntfs_volume *vol;
1N/A ntfschar *upcase;
1N/A u32 upcase_len;
1N/A
1N/A ntfs_log_trace("Entering for attribute type 0x%x.\n", type);
1N/A
1N/A if (ctx->ntfs_ino) {
1N/A vol = ctx->ntfs_ino->vol;
1N/A upcase = vol->upcase;
1N/A upcase_len = vol->upcase_len;
1N/A } else {
1N/A if (name && name != AT_UNNAMED) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A vol = NULL;
1N/A upcase = NULL;
1N/A upcase_len = 0;
1N/A }
1N/A /*
1N/A * Iterate over attributes in mft record starting at @ctx->attr, or the
1N/A * attribute following that, if @ctx->is_first is TRUE.
1N/A */
1N/A if (ctx->is_first) {
1N/A a = ctx->attr;
1N/A ctx->is_first = FALSE;
1N/A } else
1N/A a = (ATTR_RECORD*)((char*)ctx->attr +
1N/A le32_to_cpu(ctx->attr->length));
1N/A for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) {
1N/A if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec +
1N/A le32_to_cpu(ctx->mrec->bytes_allocated))
1N/A break;
1N/A ctx->attr = a;
1N/A if (((type != AT_UNUSED) && (le32_to_cpu(a->type) >
1N/A le32_to_cpu(type))) ||
1N/A (a->type == AT_END)) {
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A if (!a->length)
1N/A break;
1N/A /* If this is an enumeration return this attribute. */
1N/A if (type == AT_UNUSED)
1N/A return 0;
1N/A if (a->type != type)
1N/A continue;
1N/A /*
1N/A * If @name is AT_UNNAMED we want an unnamed attribute.
1N/A * If @name is present, compare the two names.
1N/A * Otherwise, match any attribute.
1N/A */
1N/A if (name == AT_UNNAMED) {
1N/A /* The search failed if the found attribute is named. */
1N/A if (a->name_length) {
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A } else if (name && !ntfs_names_are_equal(name, name_len,
1N/A (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)),
1N/A a->name_length, ic, upcase, upcase_len)) {
1N/A register int rc;
1N/A
1N/A rc = ntfs_names_collate(name, name_len,
1N/A (ntfschar*)((char*)a +
1N/A le16_to_cpu(a->name_offset)),
1N/A a->name_length, 1, IGNORE_CASE,
1N/A upcase, upcase_len);
1N/A /*
1N/A * If @name collates before a->name, there is no
1N/A * matching attribute.
1N/A */
1N/A if (rc == -1) {
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A /* If the strings are not equal, continue search. */
1N/A if (rc)
1N/A continue;
1N/A rc = ntfs_names_collate(name, name_len,
1N/A (ntfschar*)((char*)a +
1N/A le16_to_cpu(a->name_offset)),
1N/A a->name_length, 1, CASE_SENSITIVE,
1N/A upcase, upcase_len);
1N/A if (rc == -1) {
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A if (rc)
1N/A continue;
1N/A }
1N/A /*
1N/A * The names match or @name not present and attribute is
1N/A * unnamed. If no @val specified, we have found the attribute
1N/A * and are done.
1N/A */
1N/A if (!val)
1N/A return 0;
1N/A /* @val is present; compare values. */
1N/A else {
1N/A register int rc;
1N/A
1N/A rc = memcmp(val, (char*)a +le16_to_cpu(a->u.res.value_offset),
1N/A min(val_len,
1N/A le32_to_cpu(a->u.res.value_length)));
1N/A /*
1N/A * If @val collates before the current attribute's
1N/A * value, there is no matching attribute.
1N/A */
1N/A if (!rc) {
1N/A register u32 avl;
1N/A avl = le32_to_cpu(a->u.res.value_length);
1N/A if (val_len == avl)
1N/A return 0;
1N/A if (val_len < avl) {
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A } else if (rc < 0) {
1N/A errno = ENOENT;
1N/A return -1;
1N/A }
1N/A }
1N/A }
1N/A ntfs_log_debug("ntfs_attr_find(): File is corrupt. Run chkdsk.\n");
1N/A errno = EIO;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_external_attr_find - find an attribute in the attribute list of an inode
1N/A * @type: attribute type to find
1N/A * @name: attribute name to find (optional, i.e. NULL means don't care)
1N/A * @name_len: attribute name length (only needed if @name present)
1N/A * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
1N/A * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only)
1N/A * @val: attribute value to find (optional, resident attributes only)
1N/A * @val_len: attribute value length
1N/A * @ctx: search context with mft record and attribute to search from
1N/A *
1N/A * You shouldn't need to call this function directly. Use ntfs_attr_lookup()
1N/A * instead.
1N/A *
1N/A * Find an attribute by searching the attribute list for the corresponding
1N/A * attribute list entry. Having found the entry, map the mft record for read
1N/A * if the attribute is in a different mft record/inode, find the attribute in
1N/A * there and return it.
1N/A *
1N/A * If @type is AT_UNUSED, return the first found attribute, i.e. one can
1N/A * enumerate all attributes by setting @type to AT_UNUSED and then calling
1N/A * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to
1N/A * ENOENT to indicate that there are no more entries. During the enumeration,
1N/A * each successful call of ntfs_external_attr_find() will return the next
1N/A * attribute described by the attribute list of the base mft record described
1N/A * by the search context @ctx.
1N/A *
1N/A * If @type is AT_END, seek to the end of the base mft record ignoring the
1N/A * attribute list completely and return -1 with errno set to ENOENT. AT_END is
1N/A * not a valid attribute, its length is zero for example, thus it is safer to
1N/A * return error instead of success in this case.
1N/A *
1N/A * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
1N/A * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
1N/A * match both named and unnamed attributes.
1N/A *
1N/A * On first search @ctx->ntfs_ino must be the inode of the base mft record and
1N/A * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx().
1N/A * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too
1N/A * (@ctx->base_ntfs_ino is then the base inode).
1N/A *
1N/A * After finishing with the attribute/mft record you need to call
1N/A * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
1N/A * mapped extent inodes, etc).
1N/A *
1N/A * Return 0 if the search was successful and -1 if not, with errno set to the
1N/A * error code.
1N/A *
1N/A * On success, @ctx->attr is the found attribute, it is in mft record
1N/A * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this
1N/A * attribute with @ctx->base_* being the base mft record to which @ctx->attr
1N/A * belongs.
1N/A *
1N/A * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the
1N/A * attribute which collates just after the attribute being searched for in the
1N/A * base ntfs inode, i.e. if one wants to add the attribute to the mft record
1N/A * this is the correct place to insert it into, and if there is not enough
1N/A * space, the attribute should be placed in an extent mft record.
1N/A * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list
1N/A * at which the new attribute's attribute list entry should be inserted. The
1N/A * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL.
1N/A * The only exception to this is when @type is AT_END, in which case
1N/A * @ctx->al_entry is set to NULL also (see above).
1N/A *
1N/A * The following error codes are defined:
1N/A * ENOENT Attribute not found, not an error as such.
1N/A * EINVAL Invalid arguments.
1N/A * EIO I/O error or corrupt data structures found.
1N/A * ENOMEM Not enough memory to allocate necessary buffers.
1N/A */
1N/Astatic int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
1N/A const u32 name_len, const IGNORE_CASE_BOOL ic,
1N/A const VCN lowest_vcn, const u8 *val, const u32 val_len,
1N/A ntfs_attr_search_ctx *ctx)
1N/A{
1N/A ntfs_inode *base_ni, *ni;
1N/A ntfs_volume *vol;
1N/A ATTR_LIST_ENTRY *al_entry, *next_al_entry;
1N/A u8 *al_start, *al_end;
1N/A ATTR_RECORD *a;
1N/A ntfschar *al_name;
1N/A u32 al_name_len;
1N/A BOOL is_first_search = FALSE;
1N/A
1N/A ni = ctx->ntfs_ino;
1N/A base_ni = ctx->base_ntfs_ino;
1N/A ntfs_log_trace("Entering for inode 0x%llx, attribute type 0x%x.\n",
1N/A (unsigned long long)ni->mft_no, type);
1N/A if (!base_ni) {
1N/A /* First call happens with the base mft record. */
1N/A base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino;
1N/A ctx->base_mrec = ctx->mrec;
1N/A }
1N/A if (ni == base_ni)
1N/A ctx->base_attr = ctx->attr;
1N/A if (type == AT_END)
1N/A goto not_found;
1N/A vol = base_ni->vol;
1N/A al_start = base_ni->attr_list;
1N/A al_end = al_start + base_ni->attr_list_size;
1N/A if (!ctx->al_entry) {
1N/A ctx->al_entry = (ATTR_LIST_ENTRY*)al_start;
1N/A is_first_search = TRUE;
1N/A }
1N/A /*
1N/A * Iterate over entries in attribute list starting at @ctx->al_entry,
1N/A * or the entry following that, if @ctx->is_first is TRUE.
1N/A */
1N/A if (ctx->is_first) {
1N/A al_entry = ctx->al_entry;
1N/A ctx->is_first = FALSE;
1N/A /*
1N/A * If an enumeration and the first attribute is higher than
1N/A * the attribute list itself, need to return the attribute list
1N/A * attribute.
1N/A */
1N/A if ((type == AT_UNUSED) && is_first_search &&
1N/A le32_to_cpu(al_entry->type) >
1N/A le32_to_cpu(AT_ATTRIBUTE_LIST))
1N/A goto find_attr_list_attr;
1N/A } else {
1N/A al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +
1N/A le16_to_cpu(ctx->al_entry->length));
1N/A /*
1N/A * If this is an enumeration and the attribute list attribute
1N/A * is the next one in the enumeration sequence, just return the
1N/A * attribute list attribute from the base mft record as it is
1N/A * not listed in the attribute list itself.
1N/A */
1N/A if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) <
1N/A le32_to_cpu(AT_ATTRIBUTE_LIST) &&
1N/A le32_to_cpu(al_entry->type) >
1N/A le32_to_cpu(AT_ATTRIBUTE_LIST)) {
1N/A int rc;
1N/Afind_attr_list_attr:
1N/A
1N/A /* Check for bogus calls. */
1N/A if (name || name_len || val || val_len || lowest_vcn) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /* We want the base record. */
1N/A ctx->ntfs_ino = base_ni;
1N/A ctx->mrec = ctx->base_mrec;
1N/A ctx->is_first = TRUE;
1N/A /* Sanity checks are performed elsewhere. */
1N/A ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
1N/A le16_to_cpu(ctx->mrec->attrs_offset));
1N/A
1N/A /* Find the attribute list attribute. */
1N/A rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0,
1N/A IGNORE_CASE, NULL, 0, ctx);
1N/A
1N/A /*
1N/A * Setup the search context so the correct
1N/A * attribute is returned next time round.
1N/A */
1N/A ctx->al_entry = al_entry;
1N/A ctx->is_first = TRUE;
1N/A
1N/A /* Got it. Done. */
1N/A if (!rc)
1N/A return 0;
1N/A
1N/A /* Error! If other than not found return it. */
1N/A if (errno != ENOENT)
1N/A return rc;
1N/A
1N/A /* Not found?!? Absurd! Must be a bug... )-: */
1N/A ntfs_log_trace("BUG! Attribute list attribute not "
1N/A "found but it exists! "
1N/A "Returning error (EINVAL).\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A }
1N/A for (;; al_entry = next_al_entry) {
1N/A /* Out of bounds check. */
1N/A if ((u8*)al_entry < base_ni->attr_list ||
1N/A (u8*)al_entry > al_end)
1N/A break; /* Inode is corrupt. */
1N/A ctx->al_entry = al_entry;
1N/A /* Catch the end of the attribute list. */
1N/A if ((u8*)al_entry == al_end)
1N/A goto not_found;
1N/A if (!al_entry->length)
1N/A break;
1N/A if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
1N/A le16_to_cpu(al_entry->length) > al_end)
1N/A break;
1N/A next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
1N/A le16_to_cpu(al_entry->length));
1N/A if (type != AT_UNUSED) {
1N/A if (le32_to_cpu(al_entry->type) > le32_to_cpu(type))
1N/A goto not_found;
1N/A if (type != al_entry->type)
1N/A continue;
1N/A }
1N/A al_name_len = al_entry->name_length;
1N/A al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset);
1N/A /*
1N/A * If !@type we want the attribute represented by this
1N/A * attribute list entry.
1N/A */
1N/A if (type == AT_UNUSED)
1N/A goto is_enumeration;
1N/A /*
1N/A * If @name is AT_UNNAMED we want an unnamed attribute.
1N/A * If @name is present, compare the two names.
1N/A * Otherwise, match any attribute.
1N/A */
1N/A if (name == AT_UNNAMED) {
1N/A if (al_name_len)
1N/A goto not_found;
1N/A } else if (name && !ntfs_names_are_equal(al_name, al_name_len,
1N/A name, name_len, ic, vol->upcase,
1N/A vol->upcase_len)) {
1N/A register int rc;
1N/A
1N/A rc = ntfs_names_collate(name, name_len, al_name,
1N/A al_name_len, 1, IGNORE_CASE,
1N/A vol->upcase, vol->upcase_len);
1N/A /*
1N/A * If @name collates before al_name, there is no
1N/A * matching attribute.
1N/A */
1N/A if (rc == -1)
1N/A goto not_found;
1N/A /* If the strings are not equal, continue search. */
1N/A if (rc)
1N/A continue;
1N/A /*
1N/A * FIXME: Reverse engineering showed 0, IGNORE_CASE but
1N/A * that is inconsistent with ntfs_attr_find(). The
1N/A * subsequent rc checks were also different. Perhaps I
1N/A * made a mistake in one of the two. Need to recheck
1N/A * which is correct or at least see what is going
1N/A * on... (AIA)
1N/A */
1N/A rc = ntfs_names_collate(name, name_len, al_name,
1N/A al_name_len, 1, CASE_SENSITIVE,
1N/A vol->upcase, vol->upcase_len);
1N/A if (rc == -1)
1N/A goto not_found;
1N/A if (rc)
1N/A continue;
1N/A }
1N/A /*
1N/A * The names match or @name not present and attribute is
1N/A * unnamed. Now check @lowest_vcn. Continue search if the
1N/A * next attribute list entry still fits @lowest_vcn. Otherwise
1N/A * we have reached the right one or the search has failed.
1N/A */
1N/A if (lowest_vcn && (u8*)next_al_entry >= al_start &&
1N/A (u8*)next_al_entry + 6 < al_end &&
1N/A (u8*)next_al_entry + le16_to_cpu(
1N/A next_al_entry->length) <= al_end &&
1N/A sle64_to_cpu(next_al_entry->lowest_vcn) <=
1N/A lowest_vcn &&
1N/A next_al_entry->type == al_entry->type &&
1N/A next_al_entry->name_length == al_name_len &&
1N/A ntfs_names_are_equal((ntfschar*)((char*)
1N/A next_al_entry +
1N/A next_al_entry->name_offset),
1N/A next_al_entry->name_length,
1N/A al_name, al_name_len, CASE_SENSITIVE,
1N/A vol->upcase, vol->upcase_len))
1N/A continue;
1N/Ais_enumeration:
1N/A if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {
1N/A if (MSEQNO_LE(al_entry->mft_reference) !=
1N/A le16_to_cpu(
1N/A ni->mrec->sequence_number)) {
1N/A ntfs_log_debug("Found stale mft reference in "
1N/A "attribute list!\n");
1N/A break;
1N/A }
1N/A } else { /* Mft references do not match. */
1N/A /* Do we want the base record back? */
1N/A if (MREF_LE(al_entry->mft_reference) ==
1N/A base_ni->mft_no) {
1N/A ni = ctx->ntfs_ino = base_ni;
1N/A ctx->mrec = ctx->base_mrec;
1N/A } else {
1N/A /* We want an extent record. */
1N/A ni = ntfs_extent_inode_open(base_ni,
1N/A al_entry->mft_reference);
1N/A if (!ni) {
1N/A ntfs_log_perror("Failed to map extent "
1N/A "inode");
1N/A break;
1N/A }
1N/A ctx->ntfs_ino = ni;
1N/A ctx->mrec = ni->mrec;
1N/A }
1N/A }
1N/A a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec +
1N/A le16_to_cpu(ctx->mrec->attrs_offset));
1N/A /*
1N/A * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the
1N/A * mft record containing the attribute represented by the
1N/A * current al_entry.
1N/A *
1N/A * We could call into ntfs_attr_find() to find the right
1N/A * attribute in this mft record but this would be less
1N/A * efficient and not quite accurate as ntfs_attr_find() ignores
1N/A * the attribute instance numbers for example which become
1N/A * important when one plays with attribute lists. Also, because
1N/A * a proper match has been found in the attribute list entry
1N/A * above, the comparison can now be optimized. So it is worth
1N/A * re-implementing a simplified ntfs_attr_find() here.
1N/A *
1N/A * Use a manual loop so we can still use break and continue
1N/A * with the same meanings as above.
1N/A */
1N/Ado_next_attr_loop:
1N/A if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec +
1N/A le32_to_cpu(ctx->mrec->bytes_allocated))
1N/A break;
1N/A if (a->type == AT_END)
1N/A continue;
1N/A if (!a->length)
1N/A break;
1N/A if (al_entry->instance != a->instance)
1N/A goto do_next_attr;
1N/A /*
1N/A * If the type and/or the name are/is mismatched between the
1N/A * attribute list entry and the attribute record, there is
1N/A * corruption so we break and return error EIO.
1N/A */
1N/A if (al_entry->type != a->type)
1N/A break;
1N/A if (!ntfs_names_are_equal((ntfschar*)((char*)a +
1N/A le16_to_cpu(a->name_offset)),
1N/A a->name_length, al_name,
1N/A al_name_len, CASE_SENSITIVE,
1N/A vol->upcase, vol->upcase_len))
1N/A break;
1N/A ctx->attr = a;
1N/A /*
1N/A * If no @val specified or @val specified and it matches, we
1N/A * have found it! Also, if !@type, it is an enumeration, so we
1N/A * want the current attribute.
1N/A */
1N/A if ((type == AT_UNUSED) || !val || (!a->non_resident &&
1N/A le32_to_cpu(a->u.res.value_length) == val_len &&
1N/A !memcmp((char*)a + le16_to_cpu(a->u.res.value_offset),
1N/A val, val_len))) {
1N/A return 0;
1N/A }
1N/Ado_next_attr:
1N/A /* Proceed to the next attribute in the current mft record. */
1N/A a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
1N/A goto do_next_attr_loop;
1N/A }
1N/A if (ni != base_ni) {
1N/A ctx->ntfs_ino = base_ni;
1N/A ctx->mrec = ctx->base_mrec;
1N/A ctx->attr = ctx->base_attr;
1N/A }
1N/A ntfs_log_debug("Inode is corrupt.\n");
1N/A errno = EIO;
1N/A return -1;
1N/Anot_found:
1N/A /*
1N/A * If we were looking for AT_END or we were enumerating and reached the
1N/A * end, we reset the search context @ctx and use ntfs_attr_find() to
1N/A * seek to the end of the base mft record.
1N/A */
1N/A if (type == AT_UNUSED || type == AT_END) {
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len,
1N/A ctx);
1N/A }
1N/A /*
1N/A * The attribute wasn't found. Before we return, we want to ensure
1N/A * @ctx->mrec and @ctx->attr indicate the position at which the
1N/A * attribute should be inserted in the base mft record. Since we also
1N/A * want to preserve @ctx->al_entry we cannot reinitialize the search
1N/A * context using ntfs_attr_reinit_search_ctx() as this would set
1N/A * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see
1N/A * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve
1N/A * @ctx->al_entry as the remaining fields (base_*) are identical to
1N/A * their non base_ counterparts and we cannot set @ctx->base_attr
1N/A * correctly yet as we do not know what @ctx->attr will be set to by
1N/A * the call to ntfs_attr_find() below.
1N/A */
1N/A ctx->mrec = ctx->base_mrec;
1N/A ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
1N/A le16_to_cpu(ctx->mrec->attrs_offset));
1N/A ctx->is_first = TRUE;
1N/A ctx->ntfs_ino = ctx->base_ntfs_ino;
1N/A ctx->base_ntfs_ino = NULL;
1N/A ctx->base_mrec = NULL;
1N/A ctx->base_attr = NULL;
1N/A /*
1N/A * In case there are multiple matches in the base mft record, need to
1N/A * keep enumerating until we get an attribute not found response (or
1N/A * another error), otherwise we would keep returning the same attribute
1N/A * over and over again and all programs using us for enumeration would
1N/A * lock up in a tight loop.
1N/A */
1N/A {
1N/A int ret;
1N/A
1N/A do {
1N/A ret = ntfs_attr_find(type, name, name_len, ic, val,
1N/A val_len, ctx);
1N/A } while (!ret);
1N/A return ret;
1N/A }
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_lookup - find an attribute in an ntfs inode
1N/A * @type: attribute type to find
1N/A * @name: attribute name to find (optional, i.e. NULL means don't care)
1N/A * @name_len: attribute name length (only needed if @name present)
1N/A * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
1N/A * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only)
1N/A * @val: attribute value to find (optional, resident attributes only)
1N/A * @val_len: attribute value length
1N/A * @ctx: search context with mft record and attribute to search from
1N/A *
1N/A * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must
1N/A * be the base mft record and @ctx must have been obtained from a call to
1N/A * ntfs_attr_get_search_ctx().
1N/A *
1N/A * This function transparently handles attribute lists and @ctx is used to
1N/A * continue searches where they were left off at.
1N/A *
1N/A * If @type is AT_UNUSED, return the first found attribute, i.e. one can
1N/A * enumerate all attributes by setting @type to AT_UNUSED and then calling
1N/A * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT
1N/A * to indicate that there are no more entries. During the enumeration, each
1N/A * successful call of ntfs_attr_lookup() will return the next attribute, with
1N/A * the current attribute being described by the search context @ctx.
1N/A *
1N/A * If @type is AT_END, seek to the end of the base mft record ignoring the
1N/A * attribute list completely and return -1 with errno set to ENOENT. AT_END is
1N/A * not a valid attribute, its length is zero for example, thus it is safer to
1N/A * return error instead of success in this case. It should never be needed to
1N/A * do this, but we implement the functionality because it allows for simpler
1N/A * code inside ntfs_external_attr_find().
1N/A *
1N/A * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
1N/A * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
1N/A * match both named and unnamed attributes.
1N/A *
1N/A * After finishing with the attribute/mft record you need to call
1N/A * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
1N/A * mapped extent inodes, etc).
1N/A *
1N/A * Return 0 if the search was successful and -1 if not, with errno set to the
1N/A * error code.
1N/A *
1N/A * On success, @ctx->attr is the found attribute, it is in mft record
1N/A * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this
1N/A * attribute with @ctx->base_* being the base mft record to which @ctx->attr
1N/A * belongs. If no attribute list attribute is present @ctx->al_entry and
1N/A * @ctx->base_* are NULL.
1N/A *
1N/A * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the
1N/A * attribute which collates just after the attribute being searched for in the
1N/A * base ntfs inode, i.e. if one wants to add the attribute to the mft record
1N/A * this is the correct place to insert it into, and if there is not enough
1N/A * space, the attribute should be placed in an extent mft record.
1N/A * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list
1N/A * at which the new attribute's attribute list entry should be inserted. The
1N/A * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL.
1N/A * The only exception to this is when @type is AT_END, in which case
1N/A * @ctx->al_entry is set to NULL also (see above).
1N/A *
1N/A *
1N/A * The following error codes are defined:
1N/A * ENOENT Attribute not found, not an error as such.
1N/A * EINVAL Invalid arguments.
1N/A * EIO I/O error or corrupt data structures found.
1N/A * ENOMEM Not enough memory to allocate necessary buffers.
1N/A */
1N/Aint ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
1N/A const u32 name_len, const IGNORE_CASE_BOOL ic,
1N/A const VCN lowest_vcn, const u8 *val, const u32 val_len,
1N/A ntfs_attr_search_ctx *ctx)
1N/A{
1N/A ntfs_volume *vol;
1N/A ntfs_inode *base_ni;
1N/A
1N/A if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED &&
1N/A (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) ||
1N/A !vol->upcase || !vol->upcase_len))) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A if (ctx->base_ntfs_ino)
1N/A base_ni = ctx->base_ntfs_ino;
1N/A else
1N/A base_ni = ctx->ntfs_ino;
1N/A if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST)
1N/A return ntfs_attr_find(type, name, name_len, ic, val, val_len,
1N/A ctx);
1N/A return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn,
1N/A val, val_len, ctx);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_init_search_ctx - initialize an attribute search context
1N/A * @ctx: attribute search context to initialize
1N/A * @ni: ntfs inode with which to initialize the search context
1N/A * @mrec: mft record with which to initialize the search context
1N/A *
1N/A * Initialize the attribute search context @ctx with @ni and @mrec.
1N/A */
1N/Astatic void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx,
1N/A ntfs_inode *ni, MFT_RECORD *mrec)
1N/A{
1N/A if (!mrec)
1N/A mrec = ni->mrec;
1N/A ctx->mrec = mrec;
1N/A /* Sanity checks are performed elsewhere. */
1N/A ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
1N/A ctx->is_first = TRUE;
1N/A ctx->ntfs_ino = ni;
1N/A ctx->al_entry = NULL;
1N/A ctx->base_ntfs_ino = NULL;
1N/A ctx->base_mrec = NULL;
1N/A ctx->base_attr = NULL;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context
1N/A * @ctx: attribute search context to reinitialize
1N/A *
1N/A * Reinitialize the attribute search context @ctx.
1N/A *
1N/A * This is used when a search for a new attribute is being started to reset
1N/A * the search context to the beginning.
1N/A */
1N/Avoid ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx)
1N/A{
1N/A if (!ctx->base_ntfs_ino) {
1N/A /* No attribute list. */
1N/A ctx->is_first = TRUE;
1N/A /* Sanity checks are performed elsewhere. */
1N/A ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
1N/A le16_to_cpu(ctx->mrec->attrs_offset));
1N/A /*
1N/A * This needs resetting due to ntfs_external_attr_find() which
1N/A * can leave it set despite having zeroed ctx->base_ntfs_ino.
1N/A */
1N/A ctx->al_entry = NULL;
1N/A return;
1N/A } /* Attribute list. */
1N/A ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec);
1N/A return;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context
1N/A * @ni: ntfs inode with which to initialize the search context
1N/A * @mrec: mft record with which to initialize the search context
1N/A *
1N/A * Allocate a new attribute search context, initialize it with @ni and @mrec,
1N/A * and return it. Return NULL on error with errno set to ENOMEM.
1N/A *
1N/A * @mrec can be NULL, in which case the mft record is taken from @ni.
1N/A *
1N/A * Note: For low level utilities which know what they are doing we allow @ni to
1N/A * be NULL and @mrec to be set. Do NOT do this unless you understand the
1N/A * implications!!! For example it is no longer safe to call ntfs_attr_lookup()
1N/A * if you
1N/A */
1N/Antfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A
1N/A if (!ni && !mrec) {
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx));
1N/A if (ctx)
1N/A ntfs_attr_init_search_ctx(ctx, ni, mrec);
1N/A return ctx;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_put_search_ctx - release an attribute search context
1N/A * @ctx: attribute search context to free
1N/A *
1N/A * Release the attribute search context @ctx. This function does not change
1N/A * errno and doing nothing if NULL passed to it.
1N/A */
1N/Avoid ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
1N/A{
1N/A free(ctx);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
1N/A * @vol: ntfs volume to which the attribute belongs
1N/A * @type: attribute type which to find
1N/A *
1N/A * Search for the attribute definition record corresponding to the attribute
1N/A * @type in the $AttrDef system file.
1N/A *
1N/A * Return the attribute type definition record if found and NULL if not found
1N/A * or an error occurred. On error the error code is stored in errno. The
1N/A * following error codes are defined:
1N/A * ENOENT - The attribute @type is not specified in $AttrDef.
1N/A * EINVAL - Invalid parameters (e.g. @vol is not valid).
1N/A */
1N/AATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
1N/A const ATTR_TYPES type)
1N/A{
1N/A ATTR_DEF *ad;
1N/A
1N/A if (!vol || !vol->attrdef || !type) {
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
1N/A vol->attrdef_len && ad->type; ++ad) {
1N/A /* We haven't found it yet, carry on searching. */
1N/A if (le32_to_cpu(ad->type) < le32_to_cpu(type))
1N/A continue;
1N/A /* We found the attribute; return it. */
1N/A if (ad->type == type)
1N/A return ad;
1N/A /* We have gone too far already. No point in continuing. */
1N/A break;
1N/A }
1N/A /* Attribute not found?!? */
1N/A errno = ENOENT;
1N/A return NULL;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_size_bounds_check - check a size of an attribute type for validity
1N/A * @vol: ntfs volume to which the attribute belongs
1N/A * @type: attribute type which to check
1N/A * @size: size which to check
1N/A *
1N/A * Check whether the @size in bytes is valid for an attribute of @type on the
1N/A * ntfs volume @vol. This information is obtained from $AttrDef system file.
1N/A *
1N/A * Return 0 if valid and -1 if not valid or an error occurred. On error the
1N/A * error code is stored in errno. The following error codes are defined:
1N/A * ERANGE - @size is not valid for the attribute @type.
1N/A * ENOENT - The attribute @type is not specified in $AttrDef.
1N/A * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid).
1N/A */
1N/Aint ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type,
1N/A const s64 size)
1N/A{
1N/A ATTR_DEF *ad;
1N/A
1N/A if (size < 0) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /*
1N/A * $ATTRIBUTE_LIST should be not greater than 0x40000, but this is not
1N/A * listed in the AttrDef.
1N/A */
1N/A if (type == AT_ATTRIBUTE_LIST && size > 0x40000) {
1N/A errno = ERANGE;
1N/A return -1;
1N/A }
1N/A
1N/A ad = ntfs_attr_find_in_attrdef(vol, type);
1N/A if (!ad)
1N/A return -1;
1N/A /* We found the attribute. - Do the bounds check. */
1N/A if ((sle64_to_cpu(ad->min_size) && size <
1N/A sle64_to_cpu(ad->min_size)) ||
1N/A ((sle64_to_cpu(ad->max_size) > 0) && size >
1N/A sle64_to_cpu(ad->max_size))) {
1N/A /* @size is out of range! */
1N/A errno = ERANGE;
1N/A return -1;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident
1N/A * @vol: ntfs volume to which the attribute belongs
1N/A * @type: attribute type which to check
1N/A *
1N/A * Check whether the attribute of @type on the ntfs volume @vol is allowed to
1N/A * be non-resident. This information is obtained from $AttrDef system file.
1N/A *
1N/A * Return 0 if the attribute is allowed to be non-resident and -1 if not or an
1N/A * error occurred. On error the error code is stored in errno. The following
1N/A * error codes are defined:
1N/A * EPERM - The attribute is not allowed to be non-resident.
1N/A * ENOENT - The attribute @type is not specified in $AttrDef.
1N/A * EINVAL - Invalid parameters (e.g. @vol is not valid).
1N/A */
1N/Aint ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type)
1N/A{
1N/A ATTR_DEF *ad;
1N/A
1N/A /* Find the attribute definition record in $AttrDef. */
1N/A ad = ntfs_attr_find_in_attrdef(vol, type);
1N/A if (!ad)
1N/A return -1;
1N/A /* Check the flags and return the result. */
1N/A if (ad->flags & ATTR_DEF_RESIDENT) {
1N/A ntfs_log_trace("Attribute can't be non-resident\n");
1N/A errno = EPERM;
1N/A return -1;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_can_be_resident - check if an attribute can be resident
1N/A * @vol: ntfs volume to which the attribute belongs
1N/A * @type: attribute type which to check
1N/A *
1N/A * Check whether the attribute of @type on the ntfs volume @vol is allowed to
1N/A * be resident. This information is derived from our ntfs knowledge and may
1N/A * not be completely accurate, especially when user defined attributes are
1N/A * present. Basically we allow everything to be resident except for index
1N/A * allocation and extended attribute attributes.
1N/A *
1N/A * Return 0 if the attribute is allowed to be resident and -1 if not or an
1N/A * error occurred. On error the error code is stored in errno. The following
1N/A * error codes are defined:
1N/A * EPERM - The attribute is not allowed to be resident.
1N/A * EINVAL - Invalid parameters (e.g. @vol is not valid).
1N/A *
1N/A * Warning: In the system file $MFT the attribute $Bitmap must be non-resident
1N/A * otherwise windows will not boot (blue screen of death)! We cannot
1N/A * check for this here as we don't know which inode's $Bitmap is being
1N/A * asked about so the caller needs to special case this.
1N/A */
1N/Aint ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type)
1N/A{
1N/A if (!vol || !vol->attrdef || !type) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A if (type != AT_INDEX_ALLOCATION)
1N/A return 0;
1N/A
1N/A ntfs_log_trace("Attribute can't be resident\n");
1N/A errno = EPERM;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_make_room_for_attr - make room for an attribute inside an mft record
1N/A * @m: mft record
1N/A * @pos: position at which to make space
1N/A * @size: byte size to make available at this position
1N/A *
1N/A * @pos points to the attribute in front of which we want to make space.
1N/A *
1N/A * Return 0 on success or -1 on error. On error the error code is stored in
1N/A * errno. Possible error codes are:
1N/A * ENOSPC - There is not enough space available to complete operation. The
1N/A * caller has to make space before calling this.
1N/A * EINVAL - Input parameters were faulty.
1N/A */
1N/Aint ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size)
1N/A{
1N/A u32 biu;
1N/A
1N/A ntfs_log_trace("Entering for pos 0x%d, size %u.\n",
1N/A (int)(pos - (u8*)m), (unsigned) size);
1N/A
1N/A /* Make size 8-byte alignment. */
1N/A size = (size + 7) & ~7;
1N/A
1N/A /* Rigorous consistency checks. */
1N/A if (!m || !pos || pos < (u8*)m || pos + size >
1N/A (u8*)m + le32_to_cpu(m->bytes_allocated)) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A /* The -8 is for the attribute terminator. */
1N/A if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A /* Nothing to do. */
1N/A if (!size)
1N/A return 0;
1N/A
1N/A biu = le32_to_cpu(m->bytes_in_use);
1N/A /* Do we have enough space? */
1N/A if (biu + size > le32_to_cpu(m->bytes_allocated)) {
1N/A ntfs_log_trace("Not enough space in the MFT record\n");
1N/A errno = ENOSPC;
1N/A return -1;
1N/A }
1N/A /* Move everything after pos to pos + size. */
1N/A memmove(pos + size, pos, biu - (pos - (u8*)m));
1N/A /* Update mft record. */
1N/A m->bytes_in_use = cpu_to_le32(biu + size);
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_resident_attr_record_add - add resident attribute to inode
1N/A * @ni: opened ntfs inode to which MFT record add attribute
1N/A * @type: type of the new attribute
1N/A * @name: name of the new attribute
1N/A * @name_len: name length of the new attribute
1N/A * @val: value of the new attribute
1N/A * @size: size of new attribute (length of @val, if @val != NULL)
1N/A * @flags: flags of the new attribute
1N/A *
1N/A * Return offset to attribute from the beginning of the mft record on success
1N/A * and -1 on error. On error the error code is stored in errno.
1N/A * Possible error codes are:
1N/A * EINVAL - Invalid arguments passed to function.
1N/A * EEXIST - Attribute of such type and with same name already exists.
1N/A * EIO - I/O error occurred or damaged filesystem.
1N/A */
1N/Aint ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
1N/A ntfschar *name, u8 name_len, u8 *val, u32 size,
1N/A ATTR_FLAGS flags)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A u32 length;
1N/A ATTR_RECORD *a;
1N/A MFT_RECORD *m;
1N/A int err, offset;
1N/A ntfs_inode *base_ni;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n",
1N/A (long long) ni->mft_no, le32_to_cpu(type), le16_to_cpu(flags));
1N/A
1N/A if (!ni || (!name && name_len)) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (ntfs_attr_can_be_resident(ni->vol, type)) {
1N/A if (errno == EPERM)
1N/A ntfs_log_trace("Attribute can't be resident.\n");
1N/A else
1N/A ntfs_log_trace("ntfs_attr_can_be_resident failed.\n");
1N/A return -1;
1N/A }
1N/A
1N/A /* Locate place where record should be. */
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A /*
1N/A * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
1N/A * attribute in @ni->mrec, not any extent inode in case if @ni is base
1N/A * file record.
1N/A */
1N/A if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size,
1N/A ctx)) {
1N/A err = EEXIST;
1N/A ntfs_log_trace("Attribute already present.\n");
1N/A goto put_err_out;
1N/A }
1N/A if (errno != ENOENT) {
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A a = ctx->attr;
1N/A m = ctx->mrec;
1N/A
1N/A /* Make room for attribute. */
1N/A length = offsetof(ATTR_RECORD, u.res.resident_end) +
1N/A ((name_len * sizeof(ntfschar) + 7) & ~7) +
1N/A ((size + 7) & ~7);
1N/A if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to make room for attribute.\n");
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Setup record fields. */
1N/A offset = ((u8*)a - (u8*)m);
1N/A a->type = type;
1N/A a->length = cpu_to_le32(length);
1N/A a->non_resident = 0;
1N/A a->name_length = name_len;
1N/A a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, u.res.resident_end));
1N/A a->flags = flags;
1N/A a->instance = m->next_attr_instance;
1N/A a->u.res.value_length = cpu_to_le32(size);
1N/A a->u.res.value_offset = cpu_to_le16(length - ((size + 7) & ~7));
1N/A if (val)
1N/A memcpy((u8*)a + le16_to_cpu(a->u.res.value_offset), val, size);
1N/A else
1N/A memset((u8*)a + le16_to_cpu(a->u.res.value_offset), 0, size);
1N/A if (type == AT_FILE_NAME)
1N/A a->u.res.resident_flags = RESIDENT_ATTR_IS_INDEXED;
1N/A else
1N/A a->u.res.resident_flags = 0;
1N/A if (name_len)
1N/A memcpy((u8*)a + le16_to_cpu(a->name_offset),
1N/A name, sizeof(ntfschar) * name_len);
1N/A m->next_attr_instance =
1N/A cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff);
1N/A if (ni->nr_extents == -1)
1N/A base_ni = ni->u.base_ni;
1N/A else
1N/A base_ni = ni;
1N/A if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) {
1N/A if (ntfs_attrlist_entry_add(ni, a)) {
1N/A err = errno;
1N/A ntfs_attr_record_resize(m, a, 0);
1N/A ntfs_log_trace("Failed add attribute entry to "
1N/A "ATTRIBUTE_LIST.\n");
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A ntfs_inode_mark_dirty(ni);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return offset;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_non_resident_attr_record_add - add extent of non-resident attribute
1N/A * @ni: opened ntfs inode to which MFT record add attribute
1N/A * @type: type of the new attribute extent
1N/A * @name: name of the new attribute extent
1N/A * @name_len: name length of the new attribute extent
1N/A * @lowest_vcn: lowest vcn of the new attribute extent
1N/A * @dataruns_size: dataruns size of the new attribute extent
1N/A * @flags: flags of the new attribute extent
1N/A *
1N/A * Return offset to attribute from the beginning of the mft record on success
1N/A * and -1 on error. On error the error code is stored in errno.
1N/A * Possible error codes are:
1N/A * EINVAL - Invalid arguments passed to function.
1N/A * EEXIST - Attribute of such type, with same lowest vcn and with same
1N/A * name already exists.
1N/A * EIO - I/O error occurred or damaged filesystem.
1N/A */
1N/Aint ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
1N/A ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
1N/A ATTR_FLAGS flags)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A u32 length;
1N/A ATTR_RECORD *a;
1N/A MFT_RECORD *m;
1N/A ntfs_inode *base_ni;
1N/A int err, offset;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, "
1N/A "dataruns_size %d, flags 0x%x.\n",
1N/A (long long) ni->mft_no, le32_to_cpu(type),
1N/A (long long) lowest_vcn, dataruns_size,
1N/A le16_to_cpu(flags));
1N/A
1N/A if (!ni || dataruns_size <= 0 || (!name && name_len)) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (ntfs_attr_can_be_non_resident(ni->vol, type)) {
1N/A if (errno == EPERM)
1N/A ntfs_log_trace("Attribute can't be non resident.\n");
1N/A else
1N/A ntfs_log_trace("ntfs_attr_can_be_non_resident() "
1N/A "failed.\n");
1N/A return -1;
1N/A }
1N/A
1N/A /* Locate place where record should be. */
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A /*
1N/A * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
1N/A * attribute in @ni->mrec, not any extent inode in case if @ni is base
1N/A * file record.
1N/A */
1N/A if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0,
1N/A ctx)) {
1N/A err = EEXIST;
1N/A ntfs_log_trace("Attribute already present.\n");
1N/A goto put_err_out;
1N/A }
1N/A if (errno != ENOENT) {
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A a = ctx->attr;
1N/A m = ctx->mrec;
1N/A
1N/A /* Make room for attribute. */
1N/A dataruns_size = (dataruns_size + 7) & ~7;
1N/A length = offsetof(ATTR_RECORD, u.nonres.compressed_size) + ((sizeof(ntfschar) *
1N/A name_len + 7) & ~7) + dataruns_size +
1N/A ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ?
1N/A sizeof(a->u.nonres.compressed_size) : 0);
1N/A if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to make room for attribute.\n");
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Setup record fields. */
1N/A a->type = type;
1N/A a->length = cpu_to_le32(length);
1N/A a->non_resident = 1;
1N/A a->name_length = name_len;
1N/A a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, u.nonres.compressed_size) +
1N/A ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ?
1N/A sizeof(a->u.nonres.compressed_size) : 0));
1N/A a->flags = flags;
1N/A a->instance = m->next_attr_instance;
1N/A a->u.nonres.lowest_vcn = cpu_to_sle64(lowest_vcn);
1N/A a->u.nonres.mapping_pairs_offset = cpu_to_le16(length - dataruns_size);
1N/A a->u.nonres.compression_unit = (flags & ATTR_IS_COMPRESSED) ? 4 : 0;
1N/A /* If @lowest_vcn == 0, than setup empty attribute. */
1N/A if (!lowest_vcn) {
1N/A a->u.nonres.highest_vcn = cpu_to_sle64(-1);
1N/A a->u.nonres.allocated_size = 0;
1N/A a->u.nonres.data_size = 0;
1N/A a->u.nonres.initialized_size = 0;
1N/A /* Set empty mapping pairs. */
1N/A *((u8*)a + le16_to_cpu(a->u.nonres.mapping_pairs_offset)) = 0;
1N/A }
1N/A if (name_len)
1N/A memcpy((u8*)a + le16_to_cpu(a->name_offset),
1N/A name, sizeof(ntfschar) * name_len);
1N/A m->next_attr_instance =
1N/A cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff);
1N/A if (ni->nr_extents == -1)
1N/A base_ni = ni->u.base_ni;
1N/A else
1N/A base_ni = ni;
1N/A if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) {
1N/A if (ntfs_attrlist_entry_add(ni, a)) {
1N/A err = errno;
1N/A ntfs_attr_record_resize(m, a, 0);
1N/A ntfs_log_trace("Failed add attribute entry to "
1N/A "ATTRIBUTE_LIST.\n");
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A ntfs_inode_mark_dirty(ni);
1N/A /*
1N/A * Locate offset from start of the MFT record where new attribute is
1N/A * placed. We need relookup it, because record maybe moved during
1N/A * update of attribute list.
1N/A */
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE,
1N/A lowest_vcn, NULL, 0, ctx)) {
1N/A err = errno;
1N/A ntfs_log_trace("Attribute lookup failed. Probably leaving "
1N/A "inconsistent metadata.\n");
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A }
1N/A offset = (u8*)ctx->attr - (u8*)ctx->mrec;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return offset;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_record_rm - remove attribute extent
1N/A * @ctx: search context describing the attribute which should be removed
1N/A *
1N/A * If this function succeed, user should reinit search context if he/she wants
1N/A * use it anymore.
1N/A *
1N/A * Return 0 on success and -1 on error. On error the error code is stored in
1N/A * errno. Possible error codes are:
1N/A * EINVAL - Invalid arguments passed to function.
1N/A * EIO - I/O error occurred or damaged filesystem.
1N/A */
1N/Aint ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx)
1N/A{
1N/A ntfs_inode *base_ni, *ni;
1N/A ATTR_TYPES type;
1N/A int err;
1N/A
1N/A if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
1N/A (long long) ctx->ntfs_ino->mft_no,
1N/A (unsigned) le32_to_cpu(ctx->attr->type));
1N/A type = ctx->attr->type;
1N/A ni = ctx->ntfs_ino;
1N/A if (ctx->base_ntfs_ino)
1N/A base_ni = ctx->base_ntfs_ino;
1N/A else
1N/A base_ni = ctx->ntfs_ino;
1N/A
1N/A /* Remove attribute itself. */
1N/A if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) {
1N/A ntfs_log_trace("Couldn't remove attribute record. "
1N/A "Bug or damaged MFT record.\n");
1N/A if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST)
1N/A if (ntfs_attrlist_entry_add(ni, ctx->attr))
1N/A ntfs_log_trace("Rollback failed. Leaving "
1N/A "inconsistent metadata.\n");
1N/A err = EIO;
1N/A return -1;
1N/A }
1N/A ntfs_inode_mark_dirty(ni);
1N/A
1N/A /*
1N/A * Remove record from $ATTRIBUTE_LIST if present and we don't want
1N/A * delete $ATTRIBUTE_LIST itself.
1N/A */
1N/A if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) {
1N/A if (ntfs_attrlist_entry_rm(ctx)) {
1N/A ntfs_log_trace("Couldn't delete record from "
1N/A "$ATTRIBUTE_LIST.\n");
1N/A return -1;
1N/A }
1N/A }
1N/A
1N/A /* Post $ATTRIBUTE_LIST delete setup. */
1N/A if (type == AT_ATTRIBUTE_LIST) {
1N/A if (NInoAttrList(base_ni) && base_ni->attr_list)
1N/A free(base_ni->attr_list);
1N/A base_ni->attr_list = NULL;
1N/A NInoClearAttrList(base_ni);
1N/A NInoAttrListClearDirty(base_ni);
1N/A }
1N/A
1N/A /* Free MFT record, if it isn't contain attributes. */
1N/A if (le32_to_cpu(ctx->mrec->bytes_in_use) -
1N/A le16_to_cpu(ctx->mrec->attrs_offset) == 8) {
1N/A if (ntfs_mft_record_free(ni->vol, ni)) {
1N/A // FIXME: We need rollback here.
1N/A ntfs_log_trace("Couldn't free MFT record.\n");
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A /* Remove done if we freed base inode. */
1N/A if (ni == base_ni)
1N/A return 0;
1N/A }
1N/A
1N/A if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni))
1N/A return 0;
1N/A
1N/A /* Remove attribute list if we don't need it any more. */
1N/A if (!ntfs_attrlist_need(base_ni)) {
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A /*
1N/A * FIXME: Should we succeed here? Definitely something
1N/A * goes wrong because NInoAttrList(base_ni) returned
1N/A * that we have got attribute list.
1N/A */
1N/A ntfs_log_trace("Couldn't find attribute list. Succeed "
1N/A "anyway.\n");
1N/A return 0;
1N/A }
1N/A /* Deallocate clusters. */
1N/A if (ctx->attr->non_resident) {
1N/A runlist *al_rl;
1N/A
1N/A al_rl = ntfs_mapping_pairs_decompress(base_ni->vol,
1N/A ctx->attr, NULL);
1N/A if (!al_rl) {
1N/A ntfs_log_trace("Couldn't decompress attribute "
1N/A "list runlist. Succeed "
1N/A "anyway.\n");
1N/A return 0;
1N/A }
1N/A if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) {
1N/A ntfs_log_trace("Leaking clusters! Run chkdsk. "
1N/A "Couldn't free clusters from "
1N/A "attribute list runlist.\n");
1N/A }
1N/A free(al_rl);
1N/A }
1N/A /* Remove attribute record itself. */
1N/A if (ntfs_attr_record_rm(ctx)) {
1N/A /*
1N/A * FIXME: Should we succeed here? BTW, chkdsk doesn't
1N/A * complain if it find MFT record with attribute list,
1N/A * but without extents.
1N/A */
1N/A ntfs_log_trace("Couldn't remove attribute list. "
1N/A "Succeed anyway.\n");
1N/A return 0;
1N/A }
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_add - add attribute to inode
1N/A * @ni: opened ntfs inode to which add attribute
1N/A * @type: type of the new attribute
1N/A * @name: name in unicode of the new attribute
1N/A * @name_len: name length in unicode characters of the new attribute
1N/A * @val: value of new attribute
1N/A * @size: size of the new attribute / length of @val (if specified)
1N/A *
1N/A * @val should always be specified for always resident attributes (eg. FILE_NAME
1N/A * attribute), for attributes that can become non-resident @val can be NULL
1N/A * (eg. DATA attribute). @size can be specified even if @val is NULL, in this
1N/A * case data size will be equal to @size and initialized size will be equal
1N/A * to 0.
1N/A *
1N/A * If inode haven't got enough space to add attribute, add attribute to one of
1N/A * it extents, if no extents present or no one of them have enough space, than
1N/A * allocate new extent and add attribute to it.
1N/A *
1N/A * If on one of this steps attribute list is needed but not present, than it is
1N/A * added transparently to caller. So, this function should not be called with
1N/A * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call
1N/A * ntfs_inode_add_attrlist instead.
1N/A *
1N/A * On success return 0. On error return -1 with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
1N/A ntfschar *name, u8 name_len, u8 *val, s64 size)
1N/A{
1N/A u32 attr_rec_size;
1N/A int err, i, offset;
1N/A BOOL is_resident = TRUE;
1N/A BOOL always_non_resident = FALSE, always_resident = FALSE;
1N/A ntfs_inode *attr_ni;
1N/A ntfs_attr *na;
1N/A
1N/A if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) {
1N/A ntfs_log_trace("Invalid arguments passed.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr %x, size %lld.\n",
1N/A (long long) ni->mft_no, type, size);
1N/A
1N/A if (ni->nr_extents == -1)
1N/A ni = ni->u.base_ni;
1N/A
1N/A /* Check the attribute type and the size. */
1N/A if (ntfs_attr_size_bounds_check(ni->vol, type, size)) {
1N/A if (errno == ERANGE) {
1N/A ntfs_log_trace("Size bounds check failed.\n");
1N/A } else if (errno == ENOENT) {
1N/A ntfs_log_trace("Invalid attribute type. Aborting...\n");
1N/A errno = EIO;
1N/A }
1N/A return -1;
1N/A }
1N/A
1N/A /* Sanity checks for always resident attributes. */
1N/A if (ntfs_attr_can_be_non_resident(ni->vol, type)) {
1N/A if (errno != EPERM) {
1N/A err = errno;
1N/A ntfs_log_trace("ntfs_attr_can_be_non_resident() "
1N/A "failed.\n");
1N/A goto err_out;
1N/A }
1N/A /* @val is mandatory. */
1N/A if (!val) {
1N/A ntfs_log_trace("@val is mandatory for always resident "
1N/A "attributes.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A if (size > ni->vol->mft_record_size) {
1N/A ntfs_log_trace("Attribute is too big.\n");
1N/A errno = ERANGE;
1N/A return -1;
1N/A }
1N/A always_resident = TRUE;
1N/A }
1N/A
1N/A /* Check whether attribute can be resident. */
1N/A if (ntfs_attr_can_be_resident(ni->vol, type)) {
1N/A if (errno != EPERM) {
1N/A err = errno;
1N/A ntfs_log_trace("ntfs_attr_can_be_resident() failed.\n");
1N/A goto err_out;
1N/A }
1N/A is_resident = FALSE;
1N/A always_non_resident = TRUE;
1N/A }
1N/A
1N/Aretry:
1N/A /* Calculate attribute record size. */
1N/A if (is_resident)
1N/A attr_rec_size = offsetof(ATTR_RECORD, u.res.resident_end) +
1N/A ROUND_UP(name_len * sizeof(ntfschar), 3) +
1N/A ROUND_UP(size, 3);
1N/A else /* We add 8 for space for mapping pairs. */
1N/A attr_rec_size = offsetof(ATTR_RECORD, u.nonres.non_resident_end) +
1N/A ROUND_UP(name_len * sizeof(ntfschar), 3) + 8;
1N/A
1N/A /*
1N/A * If we have enough free space for the new attribute in the base MFT
1N/A * record, then add attribute to it.
1N/A */
1N/A if (le32_to_cpu(ni->mrec->bytes_allocated) -
1N/A le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) {
1N/A attr_ni = ni;
1N/A goto add_attr_record;
1N/A }
1N/A
1N/A /* Try to add to extent inodes. */
1N/A if (ntfs_inode_attach_all_extents(ni)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to attach all extents to inode.\n");
1N/A goto err_out;
1N/A }
1N/A for (i = 0; i < ni->nr_extents; i++) {
1N/A attr_ni = ni->u.extent_nis[i];
1N/A if (le32_to_cpu(attr_ni->mrec->bytes_allocated) -
1N/A le32_to_cpu(attr_ni->mrec->bytes_in_use) >=
1N/A attr_rec_size)
1N/A goto add_attr_record;
1N/A }
1N/A
1N/A /*
1N/A * If failed to find space for resident attribute, then try to find
1N/A * space for non resident one.
1N/A */
1N/A if (is_resident && !always_resident) {
1N/A is_resident = FALSE;
1N/A goto retry;
1N/A }
1N/A
1N/A /*
1N/A * FIXME: Try to make other attributes non-resident here. Factor out
1N/A * code from ntfs_resident_attr_resize.
1N/A */
1N/A
1N/A /* There is no extent that contain enough space for new attribute. */
1N/A if (!NInoAttrList(ni)) {
1N/A /* Add attribute list not present, add it and retry. */
1N/A if (ntfs_inode_add_attrlist(ni)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to add attribute list.\n");
1N/A goto err_out;
1N/A }
1N/A return ntfs_attr_add(ni, type, name, name_len, val, size);
1N/A }
1N/A /* Allocate new extent for attribute. */
1N/A attr_ni = ntfs_mft_record_alloc(ni->vol, ni);
1N/A if (!attr_ni) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to allocate extent record.\n");
1N/A goto err_out;
1N/A }
1N/A
1N/A /*
1N/A * Determine resident or not will be attribute using heuristics and
1N/A * calculate attribute record size. FIXME: small code duplication here.
1N/A */
1N/A if (always_resident || (!always_non_resident && size < 256)) {
1N/A is_resident = TRUE;
1N/A attr_rec_size = offsetof(ATTR_RECORD, u.res.resident_end) +
1N/A ROUND_UP(name_len * sizeof(ntfschar), 3) +
1N/A ROUND_UP(size, 3);
1N/A } else { /* We add 8 for space for mapping pairs. */
1N/A is_resident = FALSE;
1N/A attr_rec_size = offsetof(ATTR_RECORD, u.nonres.non_resident_end) +
1N/A ROUND_UP(name_len * sizeof(ntfschar), 3) + 8;
1N/A }
1N/A
1N/Aadd_attr_record:
1N/A if (is_resident) {
1N/A /* Add resident attribute. */
1N/A offset = ntfs_resident_attr_record_add(attr_ni, type, name,
1N/A name_len, val, size, 0);
1N/A if (offset < 0) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to add resident attribute.\n");
1N/A goto free_err_out;
1N/A }
1N/A return 0;
1N/A }
1N/A
1N/A /* Add non resident attribute. */
1N/A offset = ntfs_non_resident_attr_record_add(attr_ni, type, name,
1N/A name_len, 0, 8, 0);
1N/A if (offset < 0) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to add non resident attribute.\n");
1N/A goto free_err_out;
1N/A }
1N/A
1N/A /* If @size == 0, we are done. */
1N/A if (!size)
1N/A return 0;
1N/A
1N/A /* Open new attribute and resize it. */
1N/A na = ntfs_attr_open(ni, type, name, name_len);
1N/A if (!na) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to open just added attribute.\n");
1N/A goto rm_attr_err_out;
1N/A }
1N/A /* Resize and set attribute value. */
1N/A if (ntfs_attr_truncate(na, size) ||
1N/A (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to initialize just added attribute.\n");
1N/A if (ntfs_attr_rm(na))
1N/A ntfs_log_trace("Failed to remove just added attribute. "
1N/A "Probably leaving inconsistent "
1N/A "metadata.\n");
1N/A goto err_out;
1N/A }
1N/A ntfs_attr_close(na);
1N/A /* Done !*/
1N/A return 0;
1N/A
1N/Arm_attr_err_out:
1N/A /* Remove just added attribute. */
1N/A if (ntfs_attr_record_resize(attr_ni->mrec,
1N/A (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) {
1N/A ntfs_log_trace("Failed to remove just added attribute.\n");
1N/A }
1N/Afree_err_out:
1N/A /* Free MFT record, if it isn't contain attributes. */
1N/A if (le32_to_cpu(attr_ni->mrec->bytes_in_use) -
1N/A le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) {
1N/A if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) {
1N/A ntfs_log_trace("Failed to free MFT record. Leaving "
1N/A "inconsistent metadata.\n");
1N/A }
1N/A }
1N/Aerr_out:
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_rm - remove attribute from ntfs inode
1N/A * @na: opened ntfs attribute to delete
1N/A *
1N/A * Remove attribute and all it's extents from ntfs inode. If attribute was non
1N/A * resident also free all clusters allocated by attribute. This function always
1N/A * closes @na upon exit (both on success and failure).
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_rm(ntfs_attr *na)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A int ret = 0;
1N/A
1N/A if (!na) {
1N/A ntfs_log_trace("Invalid arguments passed.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
1N/A (long long) na->ni->mft_no, na->type);
1N/A
1N/A /* Free cluster allocation. */
1N/A if (NAttrNonResident(na)) {
1N/A if (ntfs_attr_map_whole_runlist(na)) {
1N/A ntfs_attr_close(na);
1N/A return -1;
1N/A }
1N/A if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) {
1N/A ntfs_log_trace("Failed to free cluster allocation. "
1N/A "Leaving inconsistent metadata.\n");
1N/A ret = -1;
1N/A }
1N/A }
1N/A
1N/A /* Search for attribute extents and remove them all. */
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx) {
1N/A ntfs_attr_close(na);
1N/A return -1;
1N/A }
1N/A while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
1N/A CASE_SENSITIVE, 0, NULL, 0, ctx)) {
1N/A if (ntfs_attr_record_rm(ctx)) {
1N/A ntfs_log_trace("Failed to remove attribute extent. "
1N/A "Leaving inconsistent metadata.\n");
1N/A ret = -1;
1N/A }
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A }
1N/A if (errno != ENOENT) {
1N/A ntfs_log_trace("Attribute lookup failed. "
1N/A "Probably leaving inconsistent metadata.\n");
1N/A ret = -1;
1N/A }
1N/A
1N/A /* Throw away now non-exist attribute. */
1N/A ntfs_attr_close(na);
1N/A /* Done. */
1N/A return ret;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_record_resize - resize an attribute record
1N/A * @m: mft record containing attribute record
1N/A * @a: attribute record to resize
1N/A * @new_size: new size in bytes to which to resize the attribute record @a
1N/A *
1N/A * Resize the attribute record @a, i.e. the resident part of the attribute, in
1N/A * the mft record @m to @new_size bytes.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * ENOSPC - Not enough space in the mft record @m to perform the resize.
1N/A * Note that on error no modifications have been performed whatsoever.
1N/A *
1N/A * Warning: If you make a record smaller without having copied all the data you
1N/A * are interested in the data may be overwritten!
1N/A */
1N/Aint ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
1N/A{
1N/A ntfs_log_trace("Entering for new_size %u.\n", (unsigned) new_size);
1N/A /* Align to 8 bytes, just in case the caller hasn't. */
1N/A new_size = (new_size + 7) & ~7;
1N/A /* If the actual attribute length has changed, move things around. */
1N/A if (new_size != le32_to_cpu(a->length)) {
1N/A u32 new_muse = le32_to_cpu(m->bytes_in_use) -
1N/A le32_to_cpu(a->length) + new_size;
1N/A /* Not enough space in this mft record. */
1N/A if (new_muse > le32_to_cpu(m->bytes_allocated)) {
1N/A errno = ENOSPC;
1N/A return -1;
1N/A }
1N/A /* Move attributes following @a to their new location. */
1N/A memmove((u8*)a + new_size, (u8*)a + le32_to_cpu(a->length),
1N/A le32_to_cpu(m->bytes_in_use) - ((u8*)a -
1N/A (u8*)m) - le32_to_cpu(a->length));
1N/A /* Adjust @m to reflect the change in used space. */
1N/A m->bytes_in_use = cpu_to_le32(new_muse);
1N/A /* Adjust @a to reflect the new size. */
1N/A if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length))
1N/A a->length = cpu_to_le32(new_size);
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_resident_attr_value_resize - resize the value of a resident attribute
1N/A * @m: mft record containing attribute record
1N/A * @a: attribute record whose value to resize
1N/A * @new_size: new size in bytes to which to resize the attribute value of @a
1N/A *
1N/A * Resize the value of the attribute @a in the mft record @m to @new_size bytes.
1N/A * If the value is made bigger, the newly "allocated" space is cleared.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * ENOSPC - Not enough space in the mft record @m to perform the resize.
1N/A * Note that on error no modifications have been performed whatsoever.
1N/A */
1N/Aint ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
1N/A const u32 new_size)
1N/A{
1N/A ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size);
1N/A
1N/A /*
1N/A * Check that the attribute name hasn't been placed after the
1N/A * attribute value. Chkdsk treat this as corruption.
1N/A */
1N/A if (a->name_length && le16_to_cpu(a->name_offset) >=
1N/A le16_to_cpu(a->u.res.value_offset)) {
1N/A ntfs_log_trace("Name is placed after the attribute value. "
1N/A "Corrupted inode. Run chkdsk. Aborting...\n");
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A /* Resize the resident part of the attribute record. */
1N/A if (ntfs_attr_record_resize(m, a, (le16_to_cpu(a->u.res.value_offset) +
1N/A new_size + 7) & ~7) < 0) {
1N/A if (errno != ENOSPC) {
1N/A int eo = errno;
1N/A ntfs_log_trace("Attribute record resize failed. "
1N/A "Aborting...\n");
1N/A errno = eo;
1N/A }
1N/A return -1;
1N/A }
1N/A /*
1N/A * If we made the attribute value bigger, clear the area between the
1N/A * old size and @new_size.
1N/A */
1N/A if (new_size > le32_to_cpu(a->u.res.value_length))
1N/A memset((u8*)a + le16_to_cpu(a->u.res.value_offset) +
1N/A le32_to_cpu(a->u.res.value_length), 0, new_size -
1N/A le32_to_cpu(a->u.res.value_length));
1N/A /* Finally update the length of the attribute value. */
1N/A a->u.res.value_length = cpu_to_le32(new_size);
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_record_move_to - move attribute record to target inode
1N/A * @ctx: attribute search context describing the attribute record
1N/A * @ni: opened ntfs inode to which move attribute record
1N/A *
1N/A * If this function succeed, user should reinit search context if he/she wants
1N/A * use it anymore.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni)
1N/A{
1N/A ntfs_attr_search_ctx *nctx;
1N/A ATTR_RECORD *a;
1N/A int err;
1N/A
1N/A if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) {
1N/A ntfs_log_trace("Invalid arguments passed.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for ctx->attr->type 0x%x, "
1N/A "ctx->ntfs_ino->mft_no 0x%llx, ni->mft_no 0x%llx.\n",
1N/A (unsigned) le32_to_cpu(ctx->attr->type),
1N/A (long long) ctx->ntfs_ino->mft_no,
1N/A (long long) ni->mft_no);
1N/A
1N/A if (ctx->ntfs_ino == ni)
1N/A return 0;
1N/A
1N/A if (!ctx->al_entry) {
1N/A ntfs_log_trace("Inode should contain attribute list to use "
1N/A "this function.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /* Find place in MFT record where attribute will be moved. */
1N/A a = ctx->attr;
1N/A nctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!nctx) {
1N/A ntfs_log_trace("Couldn't obtain search context.\n");
1N/A return -1;
1N/A }
1N/A /*
1N/A * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
1N/A * attribute in @ni->mrec, not any extent inode in case if @ni is base
1N/A * file record.
1N/A */
1N/A if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu(
1N/A a->name_offset)), a->name_length, CASE_SENSITIVE, NULL,
1N/A 0, nctx)) {
1N/A ntfs_log_trace("Attribute of such type, with same name already "
1N/A "present in this MFT record.\n");
1N/A err = EEXIST;
1N/A goto put_err_out;
1N/A }
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_debug("Attribute lookup failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Make space and move attribute. */
1N/A if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr,
1N/A le32_to_cpu(a->length))) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't make space for attribute.\n");
1N/A goto put_err_out;
1N/A }
1N/A memcpy(nctx->attr, a, le32_to_cpu(a->length));
1N/A nctx->attr->instance = nctx->mrec->next_attr_instance;
1N/A nctx->mrec->next_attr_instance = cpu_to_le16(
1N/A (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff);
1N/A ntfs_attr_record_resize(ctx->mrec, a, 0);
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A ntfs_inode_mark_dirty(ni);
1N/A
1N/A /* Update attribute list. */
1N/A ctx->al_entry->mft_reference =
1N/A MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
1N/A ctx->al_entry->instance = nctx->attr->instance;
1N/A ntfs_attrlist_mark_dirty(ni);
1N/A
1N/A ntfs_attr_put_search_ctx(nctx);
1N/A return 0;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(nctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_record_move_away - move away attribute record from it's mft record
1N/A * @ctx: attribute search context describing the attribute record
1N/A * @extra: minimum amount of free space in the new holder of record
1N/A *
1N/A * New attribute record holder must have free @extra bytes after moving
1N/A * attribute record to it.
1N/A *
1N/A * If this function succeed, user should reinit search context if he/she wants
1N/A * use it anymore.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra)
1N/A{
1N/A ntfs_inode *base_ni, *ni;
1N/A MFT_RECORD *m;
1N/A int i;
1N/A
1N/A if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) {
1N/A ntfs_log_trace("Invalid arguments passed.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for attr 0x%x, inode 0x%llx.\n",
1N/A (unsigned) le32_to_cpu(ctx->attr->type),
1N/A (long long) ctx->ntfs_ino->mft_no);
1N/A
1N/A if (ctx->ntfs_ino->nr_extents == -1)
1N/A base_ni = ctx->base_ntfs_ino;
1N/A else
1N/A base_ni = ctx->ntfs_ino;
1N/A
1N/A if (!NInoAttrList(base_ni)) {
1N/A ntfs_log_trace("Inode should contain attribute list to use "
1N/A "this function.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) {
1N/A ntfs_log_trace("Couldn't attach extent inode.\n");
1N/A return -1;
1N/A }
1N/A
1N/A /* Walk through all extents and try to move attribute to them. */
1N/A for (i = 0; i < base_ni->nr_extents; i++) {
1N/A ni = base_ni->u.extent_nis[i];
1N/A m = ni->mrec;
1N/A
1N/A if (ctx->ntfs_ino->mft_no == ni->mft_no)
1N/A continue;
1N/A
1N/A if (le32_to_cpu(m->bytes_allocated) -
1N/A le32_to_cpu(m->bytes_in_use) <
1N/A le32_to_cpu(ctx->attr->length) + extra)
1N/A continue;
1N/A
1N/A /*
1N/A * ntfs_attr_record_move_to can fail if extent with other lowest
1N/A * VCN already present in inode we trying move record to. So,
1N/A * do not return error.
1N/A */
1N/A if (!ntfs_attr_record_move_to(ctx, ni))
1N/A return 0;
1N/A }
1N/A
1N/A /*
1N/A * Failed to move attribute to one of the current extents, so allocate
1N/A * new extent and move attribute to it.
1N/A */
1N/A ni = ntfs_mft_record_alloc(base_ni->vol, base_ni);
1N/A if (!ni) {
1N/A ntfs_log_trace("Couldn't allocate new MFT record.\n");
1N/A return -1;
1N/A }
1N/A if (ntfs_attr_record_move_to(ctx, ni)) {
1N/A ntfs_log_trace("Couldn't move attribute to new MFT record.\n");
1N/A return -1;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
1N/A * @na: open ntfs attribute to make non-resident
1N/A * @ctx: ntfs search context describing the attribute
1N/A *
1N/A * Convert a resident ntfs attribute to a non-resident one.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code. The
1N/A * following error codes are defined:
1N/A * EPERM - The attribute is not allowed to be non-resident.
1N/A * TODO: others...
1N/A *
1N/A * NOTE to self: No changes in the attribute list are required to move from
1N/A * a resident to a non-resident attribute.
1N/A *
1N/A * Warning: We do not set the inode dirty and we do not write out anything!
1N/A * We expect the caller to do this as this is a fairly low level
1N/A * function and it is likely there will be further changes made.
1N/A */
1N/Astatic int ntfs_attr_make_non_resident(ntfs_attr *na,
1N/A ntfs_attr_search_ctx *ctx)
1N/A{
1N/A s64 new_allocated_size, bw;
1N/A ntfs_volume *vol = na->ni->vol;
1N/A ATTR_REC *a = ctx->attr;
1N/A runlist *rl;
1N/A int mp_size, mp_ofs, name_ofs, arec_size;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
1N/A long)na->ni->mft_no, na->type);
1N/A
1N/A /* Some preliminary sanity checking. */
1N/A if (NAttrNonResident(na)) {
1N/A ntfs_log_trace("Eeek! Trying to make non-resident attribute "
1N/A "non-resident. Aborting...\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /* Check that the attribute is allowed to be non-resident. */
1N/A if (ntfs_attr_can_be_non_resident(vol, na->type))
1N/A return -1;
1N/A
1N/A /*
1N/A * Check that the attribute name hasn't been placed after the
1N/A * attribute value. Chkdsk treat this as corruption.
1N/A */
1N/A if (a->name_length && le16_to_cpu(a->name_offset) >=
1N/A le16_to_cpu(a->u.res.value_offset)) {
1N/A ntfs_log_trace("Name is placed after the attribute value. "
1N/A "Corrupted inode. Run chkdsk. Aborting...\n");
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A new_allocated_size = (le32_to_cpu(a->u.res.value_length) + vol->cluster_size
1N/A - 1) & ~(vol->cluster_size - 1);
1N/A
1N/A if (new_allocated_size > 0) {
1N/A /* Start by allocating clusters to hold the attribute value. */
1N/A rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >>
1N/A vol->cluster_size_bits, -1, DATA_ZONE);
1N/A if (!rl) {
1N/A if (errno != ENOSPC) {
1N/A ntfs_log_trace("Eeek! Failed to allocate "
1N/A "cluster(s). Aborting...\n");
1N/A }
1N/A return -1;
1N/A }
1N/A } else
1N/A rl = NULL;
1N/A /*
1N/A * Setup the in-memory attribute structure to be non-resident so that
1N/A * we can use ntfs_attr_pwrite().
1N/A */
1N/A NAttrSetNonResident(na);
1N/A na->rl = rl;
1N/A na->allocated_size = new_allocated_size;
1N/A na->data_size = na->initialized_size = le32_to_cpu(a->u.res.value_length);
1N/A /*
1N/A * FIXME: For now just clear all of these as we don't support them when
1N/A * writing.
1N/A */
1N/A NAttrClearCompressed(na);
1N/A NAttrClearSparse(na);
1N/A NAttrClearEncrypted(na);
1N/A
1N/A if (rl) {
1N/A /* Now copy the attribute value to the allocated cluster(s). */
1N/A bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->u.res.value_length),
1N/A (u8*)a + le16_to_cpu(a->u.res.value_offset));
1N/A if (bw != le32_to_cpu(a->u.res.value_length)) {
1N/A ntfs_log_debug("Failed to write out attribute value "
1N/A "(bw = %lli, errno = %i). "
1N/A "Aborting...\n", (long long)bw, errno);
1N/A if (bw >= 0)
1N/A errno = EIO;
1N/A goto cluster_free_err_out;
1N/A }
1N/A }
1N/A /* Determine the size of the mapping pairs array. */
1N/A mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0);
1N/A if (mp_size < 0) {
1N/A ntfs_log_debug("Failed to get size for mapping pairs array. "
1N/A "Aborting...\n");
1N/A goto cluster_free_err_out;
1N/A }
1N/A /* Calculate new offsets for the name and the mapping pairs array. */
1N/A name_ofs = (sizeof(ATTR_REC) - sizeof(a->u.nonres.compressed_size) + 7) & ~7;
1N/A mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
1N/A /*
1N/A * Determine the size of the resident part of the non-resident
1N/A * attribute record. (Not compressed thus no compressed_size element
1N/A * present.)
1N/A */
1N/A arec_size = (mp_ofs + mp_size + 7) & ~7;
1N/A
1N/A /* Resize the resident part of the attribute record. */
1N/A if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) {
1N/A if (errno != ENOSPC) {
1N/A ntfs_log_trace("Failed to resize attribute record. "
1N/A "Aborting...\n");
1N/A }
1N/A goto cluster_free_err_out;
1N/A }
1N/A
1N/A /*
1N/A * Convert the resident part of the attribute record to describe a
1N/A * non-resident attribute.
1N/A */
1N/A a->non_resident = 1;
1N/A
1N/A /* Move the attribute name if it exists and update the offset. */
1N/A if (a->name_length)
1N/A memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
1N/A a->name_length * sizeof(ntfschar));
1N/A a->name_offset = cpu_to_le16(name_ofs);
1N/A
1N/A /* Update the flags to match the in-memory ones. */
1N/A a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED |
1N/A ATTR_COMPRESSION_MASK);
1N/A
1N/A /* Setup the fields specific to non-resident attributes. */
1N/A a->u.nonres.lowest_vcn = cpu_to_sle64(0);
1N/A a->u.nonres.highest_vcn = cpu_to_sle64((new_allocated_size - 1) >>
1N/A vol->cluster_size_bits);
1N/A
1N/A a->u.nonres.mapping_pairs_offset = cpu_to_le16(mp_ofs);
1N/A
1N/A a->u.nonres.compression_unit = 0;
1N/A
1N/A memset(&a->u.nonres.reserved1, 0, sizeof(a->u.nonres.reserved1));
1N/A
1N/A a->u.nonres.allocated_size = cpu_to_sle64(new_allocated_size);
1N/A a->u.nonres.data_size = a->u.nonres.initialized_size = cpu_to_sle64(na->data_size);
1N/A
1N/A /* Generate the mapping pairs array in the attribute record. */
1N/A if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs,
1N/A rl, 0, NULL) < 0) {
1N/A // FIXME: Eeek! We need rollback! (AIA)
1N/A ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving "
1N/A "corrupt attribute record on disk. In memory "
1N/A "runlist is still intact! Error code is %i. "
1N/A "FIXME: Need to rollback instead!\n", errno);
1N/A return -1;
1N/A }
1N/A
1N/A /* Done! */
1N/A return 0;
1N/A
1N/Acluster_free_err_out:
1N/A if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0)
1N/A ntfs_log_trace("Failed to release allocated clusters in error "
1N/A "code path. Leaving inconsistent metadata...\n");
1N/A NAttrClearNonResident(na);
1N/A na->allocated_size = na->data_size;
1N/A na->rl = NULL;
1N/A free(rl);
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_resident_attr_resize - resize a resident, open ntfs attribute
1N/A * @na: resident ntfs attribute to resize
1N/A * @newsize: new size (in bytes) to which to resize the attribute
1N/A *
1N/A * Change the size of a resident, open ntfs attribute @na to @newsize bytes.
1N/A *
1N/A * On success return 0 and on error return -1 with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * ENOMEM - Not enough memory to complete operation.
1N/A * ERANGE - @newsize is not valid for the attribute type of @na.
1N/A * ENOSPC - There is no enough space on the volume to allocate
1N/A * new clusters or in base mft to resize $ATTRIBUTE_LIST.
1N/A * EOVERFLOW - Resident attribute can not become non resident and
1N/A * already filled whole MFT record, but had not reached
1N/A * @newsize bytes length.
1N/A */
1N/Astatic int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A ntfs_volume *vol;
1N/A ntfs_inode *ni;
1N/A int err;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, new size %lld.\n",
1N/A (unsigned long long)na->ni->mft_no, na->type,
1N/A (long long)newsize);
1N/A
1N/A /* Get the attribute record that needs modification. */
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0,
1N/A ctx)) {
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A vol = na->ni->vol;
1N/A /*
1N/A * Check the attribute type and the corresponding minimum and maximum
1N/A * sizes against @newsize and fail if @newsize is out of bounds.
1N/A */
1N/A if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
1N/A err = errno;
1N/A if (err == ERANGE) {
1N/A ntfs_log_trace("Size bounds check failed. "
1N/A "Aborting...\n");
1N/A } else if (err == ENOENT)
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A /*
1N/A * If @newsize is bigger than the MFT record we need to make the
1N/A * attribute non-resident if the attribute type supports it. If it is
1N/A * smaller we can go ahead and attempt the resize.
1N/A */
1N/A if (newsize < vol->mft_record_size) {
1N/A /* Perform the resize of the attribute record. */
1N/A if (!ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
1N/A newsize)) {
1N/A /* Update attribute size everywhere. */
1N/A na->data_size = na->initialized_size = newsize;
1N/A na->allocated_size = ROUND_UP(newsize, 3);
1N/A if (NAttrCompressed(na) || NAttrSparse(na))
1N/A na->compressed_size = na->allocated_size;
1N/A if (na->type == AT_DATA && na->name == AT_UNNAMED) {
1N/A na->ni->data_size = na->data_size;
1N/A na->ni->allocated_size = na->allocated_size;
1N/A NInoFileNameSetDirty(na->ni);
1N/A }
1N/A goto resize_done;
1N/A }
1N/A /* Error! If not enough space, just continue. */
1N/A if (errno != ENOSPC) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to resize resident part "
1N/A "of attribute. Aborting...\n");
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A /* There is not enough space in the MFT record to perform the resize. */
1N/A
1N/A /* Make the attribute non-resident if possible. */
1N/A if (!ntfs_attr_make_non_resident(na, ctx)) {
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A /* Resize non-resident attribute */
1N/A return ntfs_attr_truncate(na, newsize);
1N/A } else if (errno != ENOSPC && errno != EPERM) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to make attribute non-resident. "
1N/A "Aborting...\n");
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Try to make other attributes non-resident and retry each time. */
1N/A ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec);
1N/A while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {
1N/A ntfs_attr *tna;
1N/A ATTR_RECORD *a;
1N/A
1N/A a = ctx->attr;
1N/A if (a->non_resident)
1N/A continue;
1N/A
1N/A /*
1N/A * Check out whether convert is reasonable. Assume that mapping
1N/A * pairs will take 8 bytes.
1N/A */
1N/A if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD,
1N/A u.nonres.compressed_size) + ROUND_UP(a->name_length *
1N/A sizeof(ntfschar), 3) + 8)
1N/A continue;
1N/A
1N/A tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a +
1N/A le16_to_cpu(a->name_offset)), a->name_length);
1N/A if (!tna) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't open attribute.\n");
1N/A goto put_err_out;
1N/A }
1N/A if (ntfs_attr_make_non_resident(tna, ctx)) {
1N/A ntfs_attr_close(tna);
1N/A continue;
1N/A }
1N/A ntfs_inode_mark_dirty(tna->ni);
1N/A ntfs_attr_close(tna);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return ntfs_resident_attr_resize(na, newsize);
1N/A }
1N/A /* Check whether error occurred. */
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* We can't move out attribute list, thus move out others. */
1N/A if (na->type == AT_ATTRIBUTE_LIST) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD,
1N/A u.nonres.non_resident_end) + 8)) {
1N/A ntfs_log_trace("Couldn't free space in the MFT record "
1N/A "to make attribute list non "
1N/A "resident.\n");
1N/A return -1;
1N/A }
1N/A return ntfs_resident_attr_resize(na, newsize);
1N/A }
1N/A
1N/A /*
1N/A * Move the attribute to a new MFT record, creating an attribute list
1N/A * attribute or modifying it if it is already present.
1N/A */
1N/A
1N/A /* Point search context back to attribute which we need resize. */
1N/A ntfs_attr_init_search_ctx(ctx, na->ni, NULL);
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /*
1N/A * Force index allocation creation instead of moving out index root
1N/A * from the base MFT record.
1N/A */
1N/A if (na->type == AT_INDEX_ROOT && na->data_size > sizeof(INDEX_ROOT) +
1N/A sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) {
1N/A INDEX_ROOT *ir;
1N/A
1N/A ir = (INDEX_ROOT*)((u8*)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A if (!(ir->index.flags & LARGE_INDEX)) {
1N/A err = EOVERFLOW;
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A
1N/A /*
1N/A * Check whether attribute is already single in the this MFT record.
1N/A * 8 added for the attribute terminator.
1N/A */
1N/A if (le32_to_cpu(ctx->mrec->bytes_in_use) ==
1N/A le16_to_cpu(ctx->mrec->attrs_offset) +
1N/A le32_to_cpu(ctx->attr->length) + 8) {
1N/A err = EOVERFLOW;
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Add attribute list if not present. */
1N/A if (na->ni->nr_extents == -1)
1N/A ni = na->ni->u.base_ni;
1N/A else
1N/A ni = na->ni;
1N/A if (!NInoAttrList(ni)) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (ntfs_inode_add_attrlist(ni))
1N/A return -1;
1N/A return ntfs_resident_attr_resize(na, newsize);
1N/A }
1N/A /* Allocate new MFT record. */
1N/A ni = ntfs_mft_record_alloc(vol, ni);
1N/A if (!ni) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't allocate new MFT record.\n");
1N/A goto put_err_out;
1N/A }
1N/A /* Move attribute to it. */
1N/A if (ntfs_attr_record_move_to(ctx, ni)) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't move attribute to new MFT record.\n");
1N/A goto put_err_out;
1N/A }
1N/A /* Update ntfs attribute. */
1N/A if (na->ni->nr_extents == -1)
1N/A na->ni = ni;
1N/A
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A /* Try to perform resize once again. */
1N/A return ntfs_resident_attr_resize(na, newsize);
1N/A
1N/Aresize_done:
1N/A /*
1N/A * Set the inode (and its base inode if it exists) dirty so it is
1N/A * written out later.
1N/A */
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A /* Done! */
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return 0;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_make_resident - convert a non-resident to a resident attribute
1N/A * @na: open ntfs attribute to make resident
1N/A * @ctx: ntfs search context describing the attribute
1N/A *
1N/A * Convert a non-resident ntfs attribute to a resident one.
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code. The
1N/A * following error codes are defined:
1N/A * EINVAL - Invalid arguments passed.
1N/A * EPERM - The attribute is not allowed to be resident.
1N/A * EIO - I/O error, damaged inode or bug.
1N/A * ENOSPC - There is no enough space to perform conversion.
1N/A * EOPNOTSUPP - Requested conversion is not supported yet.
1N/A *
1N/A * Warning: We do not set the inode dirty and we do not write out anything!
1N/A * We expect the caller to do this as this is a fairly low level
1N/A * function and it is likely there will be further changes made.
1N/A */
1N/Astatic int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
1N/A{
1N/A ntfs_volume *vol = na->ni->vol;
1N/A ATTR_REC *a = ctx->attr;
1N/A int name_ofs, val_ofs;
1N/A s64 arec_size, bytes_read;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
1N/A long)na->ni->mft_no, na->type);
1N/A
1N/A /* Should be called for the first extent of the attribute. */
1N/A if (sle64_to_cpu(a->u.nonres.lowest_vcn)) {
1N/A ntfs_log_trace("Should be called for the first extent of the "
1N/A "attribute. Aborting...\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /* Some preliminary sanity checking. */
1N/A if (!NAttrNonResident(na)) {
1N/A ntfs_log_trace("Trying to make resident attribute resident. "
1N/A "Aborting...\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */
1N/A if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) {
1N/A errno = EPERM;
1N/A return -1;
1N/A }
1N/A
1N/A /* Check that the attribute is allowed to be resident. */
1N/A if (ntfs_attr_can_be_resident(vol, na->type))
1N/A return -1;
1N/A
1N/A /*
1N/A * Check that the attribute name hasn't been placed after the
1N/A * mapping pairs array. Chkdsk treat this as corruption.
1N/A */
1N/A if (a->name_length && le16_to_cpu(a->name_offset) >=
1N/A le16_to_cpu(a->u.nonres.mapping_pairs_offset)) {
1N/A ntfs_log_trace("Damaged attribute. Name is placed after the "
1N/A "mapping pairs array. Run chkdsk. Aborting.\n");
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A if (NAttrCompressed(na) || NAttrEncrypted(na)) {
1N/A ntfs_log_trace("Making compressed or encrypted files resident "
1N/A "is not implemented yet.\n");
1N/A errno = EOPNOTSUPP;
1N/A return -1;
1N/A }
1N/A
1N/A /* Work out offsets into and size of the resident attribute. */
1N/A name_ofs = 24; /* = sizeof(resident_ATTR_REC); */
1N/A val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
1N/A arec_size = (val_ofs + na->data_size + 7) & ~7;
1N/A
1N/A /* Sanity check the size before we start modifying the attribute. */
1N/A if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) +
1N/A arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) {
1N/A ntfs_log_trace("Not enough space to make attribute resident\n");
1N/A errno = ENOSPC;
1N/A return -1;
1N/A }
1N/A
1N/A /* Read and cache the whole runlist if not already done. */
1N/A if (ntfs_attr_map_whole_runlist(na))
1N/A return -1;
1N/A
1N/A /* Move the attribute name if it exists and update the offset. */
1N/A if (a->name_length) {
1N/A memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
1N/A a->name_length * sizeof(ntfschar));
1N/A }
1N/A a->name_offset = cpu_to_le16(name_ofs);
1N/A
1N/A /* Resize the resident part of the attribute record. */
1N/A if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) {
1N/A /*
1N/A * Bug, because ntfs_attr_record_resize should not fail (we
1N/A * already checked that attribute fits MFT record).
1N/A */
1N/A ntfs_log_error("BUG! Failed to resize attribute record. "
1N/A "Please report to the %s. Aborting...\n",
1N/A NTFS_DEV_LIST);
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A /* Convert the attribute record to describe a resident attribute. */
1N/A a->non_resident = 0;
1N/A a->flags = 0;
1N/A a->u.res.value_length = cpu_to_le32(na->data_size);
1N/A a->u.res.value_offset = cpu_to_le16(val_ofs);
1N/A /*
1N/A * File names cannot be non-resident so we would never see this here
1N/A * but at least it serves as a reminder that there may be attributes
1N/A * for which we do need to set this flag. (AIA)
1N/A */
1N/A if (a->type == AT_FILE_NAME)
1N/A a->u.res.resident_flags = RESIDENT_ATTR_IS_INDEXED;
1N/A else
1N/A a->u.res.resident_flags = 0;
1N/A a->u.res.reservedR = 0;
1N/A
1N/A /* Sanity fixup... Shouldn't really happen. (AIA) */
1N/A if (na->initialized_size > na->data_size)
1N/A na->initialized_size = na->data_size;
1N/A
1N/A /* Copy data from run list to resident attribute value. */
1N/A bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size,
1N/A (u8*)a + val_ofs);
1N/A if (bytes_read != na->initialized_size) {
1N/A if (bytes_read >= 0)
1N/A errno = EIO;
1N/A ntfs_log_trace("Eeek! Failed to read attribute data. Leaving "
1N/A "inconsistent metadata. Run chkdsk. "
1N/A "Aborting...\n");
1N/A return -1;
1N/A }
1N/A
1N/A /* Clear memory in gap between initialized_size and data_size. */
1N/A if (na->initialized_size < na->data_size)
1N/A memset((u8*)a + val_ofs + na->initialized_size, 0,
1N/A na->data_size - na->initialized_size);
1N/A
1N/A /*
1N/A * Deallocate clusters from the runlist.
1N/A *
1N/A * NOTE: We can use ntfs_cluster_free() because we have already mapped
1N/A * the whole run list and thus it doesn't matter that the attribute
1N/A * record is in a transiently corrupted state at this moment in time.
1N/A */
1N/A if (ntfs_cluster_free(vol, na, 0, -1) < 0) {
1N/A ntfs_log_perror("Eeek! Failed to release allocated clusters");
1N/A ntfs_log_trace("Ignoring error and leaving behind wasted "
1N/A "clusters.\n");
1N/A }
1N/A
1N/A /* Throw away the now unused runlist. */
1N/A free(na->rl);
1N/A na->rl = NULL;
1N/A
1N/A /* Update in-memory struct ntfs_attr. */
1N/A NAttrClearNonResident(na);
1N/A NAttrClearCompressed(na);
1N/A NAttrClearSparse(na);
1N/A NAttrClearEncrypted(na);
1N/A na->initialized_size = na->data_size;
1N/A na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7;
1N/A na->compression_block_size = 0;
1N/A na->compression_block_size_bits = na->compression_block_clusters = 0;
1N/A return 0;
1N/A}
1N/A
1N/A#define NTFS_VCN_DELETE_MARK -2
1N/A/**
1N/A * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute
1N/A * @na: non-resident ntfs open attribute for which we need update
1N/A * @from_vcn: update runlist starting this VCN
1N/A *
1N/A * Build mapping pairs from @na->rl and write them to the disk. Also, this
1N/A * function updates sparse bit, allocated and compressed size (allocates/frees
1N/A * space for this field if required).
1N/A *
1N/A * @na->allocated_size should be set to correct value for the new runlist before
1N/A * call to this function. Vice-versa @na->compressed_size will be calculated and
1N/A * set to correct value during this function.
1N/A *
1N/A * New runlist should be fully formed starting @from_vcn. Runs before @from_vcn
1N/A * can be mapped or not, but on-disk structures should not be modified before
1N/A * call to this function so they can be mapped if necessary.
1N/A *
1N/A * FIXME: Make it O(1) for sparse files too, not only for normal.
1N/A *
1N/A * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define.
1N/A *
1N/A * NOTE: Be careful in the future with updating bits on compressed files (at
1N/A * present assumed that on-disk flag is already set/cleared before call to
1N/A * this function).
1N/A *
1N/A * On success return 0 and on error return -1 with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * EINVAL - Invalid arguments passed.
1N/A * ENOMEM - Not enough memory to complete operation.
1N/A * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST
1N/A * or there is no free MFT records left to allocate.
1N/A */
1N/Aint ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A ntfs_inode *ni, *base_ni;
1N/A MFT_RECORD *m;
1N/A ATTR_RECORD *a;
1N/A VCN stop_vcn;
1N/A int err, mp_size, cur_max_mp_size, exp_max_mp_size;
1N/A BOOL finished_build;
1N/A
1N/Aretry:
1N/A if (!na || !na->rl) {
1N/A ntfs_log_trace("Invalid parameters passed.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (!NAttrNonResident(na)) {
1N/A ntfs_log_trace("Attribute should be non resident.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, from vcn 0x%lld."
1N/A "\n", (unsigned long long)na->ni->mft_no, na->type,
1N/A from_vcn);
1N/A
1N/A if (na->ni->nr_extents == -1)
1N/A base_ni = na->ni->u.base_ni;
1N/A else
1N/A base_ni = na->ni;
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(base_ni, NULL);
1N/A if (!ctx) {
1N/A ntfs_log_trace("Couldn't get search context.\n");
1N/A return -1;
1N/A }
1N/A
1N/A /* Fill attribute records with new mapping pairs. */
1N/A stop_vcn = 0;
1N/A finished_build = FALSE;
1N/A while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
1N/A CASE_SENSITIVE, ctx->is_first ? 0 : from_vcn,
1N/A NULL, 0, ctx)) {
1N/A a = ctx->attr;
1N/A m = ctx->mrec;
1N/A /*
1N/A * If runlist is updating not from the beginning, then set
1N/A * @stop_vcn properly, i.e. to the lowest vcn of record that
1N/A * contain @from_vcn. Also we do not need @from_vcn anymore,
1N/A * set it to 0 to make ntfs_attr_lookup enumerate attributes.
1N/A */
1N/A if (from_vcn && a->u.nonres.lowest_vcn) {
1N/A LCN first_lcn;
1N/A
1N/A stop_vcn = sle64_to_cpu(a->u.nonres.lowest_vcn);
1N/A from_vcn = 0;
1N/A /*
1N/A * Check whether the first run we need to update is
1N/A * the last run in runlist, if so, then deallocate
1N/A * all attribute extents starting this one.
1N/A */
1N/A first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn);
1N/A if (first_lcn == LCN_EINVAL) {
1N/A ntfs_log_trace("BUG! Incorrect runlist.\n");
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A if (first_lcn == LCN_ENOENT ||
1N/A first_lcn == LCN_RL_NOT_MAPPED)
1N/A finished_build = TRUE;
1N/A }
1N/A
1N/A /*
1N/A * Check whether we finished mapping pairs build, if so mark
1N/A * extent as need to delete (by setting highest vcn to
1N/A * NTFS_VCN_DELETE_MARK (-2), we shall check it later and
1N/A * delete extent) and continue search.
1N/A */
1N/A if (finished_build) {
1N/A ntfs_log_trace("Mark attr 0x%x for delete in inode "
1N/A "0x%llx.\n", (unsigned)le32_to_cpu(
1N/A a->type), ctx->ntfs_ino->mft_no);
1N/A a->u.nonres.highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK);
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A continue;
1N/A }
1N/A
1N/A /*
1N/A * Check that the attribute name hasn't been placed after the
1N/A * mapping pairs array. Windows treat this as a corruption.
1N/A */
1N/A if (a->name_length) {
1N/A if (le16_to_cpu(a->name_offset) >=
1N/A le16_to_cpu(a->u.nonres.mapping_pairs_offset)) {
1N/A ntfs_log_error("Damaged attribute. Name is "
1N/A "placed after the mapping "
1N/A "pairs array. Run chkdsk.\n");
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A /*
1N/A * If we in the first extent, then set/clean sparse bit,
1N/A * update allocated and compressed size.
1N/A */
1N/A if (!a->u.nonres.lowest_vcn) {
1N/A int sparse;
1N/A
1N/A /* Update allocated size. */
1N/A a->u.nonres.allocated_size = cpu_to_sle64(na->allocated_size);
1N/A /*
1N/A * Check whether part of runlist we are updating is
1N/A * sparse.
1N/A */
1N/A sparse = ntfs_rl_sparse(na->rl);
1N/A if (sparse == -1) {
1N/A ntfs_log_trace("Bad runlist.\n");
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A /*
1N/A * If new part or on-disk attribute is not sparse, then
1N/A * we should fully map runlist to make final decision.
1N/A */
1N/A if (sparse || (a->flags & ATTR_IS_SPARSE)) {
1N/A if (from_vcn && ntfs_attr_map_runlist_range(na,
1N/A 0, from_vcn - 1)) {
1N/A ntfs_log_trace("Failed to map runlist "
1N/A "before @from_vcn.\n");
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A /*
1N/A * Reconsider whether whole runlist is sparse
1N/A * if new part is not.
1N/A */
1N/A if (!sparse) {
1N/A sparse = ntfs_rl_sparse(na->rl);
1N/A if (sparse == -1) {
1N/A ntfs_log_trace("Bad "
1N/A "runlist.\n");
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A }
1N/A /* Attribute becomes sparse/compressed. */
1N/A if (sparse && !(a->flags & (ATTR_IS_SPARSE |
1N/A ATTR_IS_COMPRESSED))) {
1N/A /*
1N/A * We need to move attribute to another mft
1N/A * record, if attribute is to small to add
1N/A * compressed_size field to it and we have no
1N/A * free space in the current mft record.
1N/A */
1N/A if ((le32_to_cpu(a->length) - le16_to_cpu(
1N/A a->u.nonres.mapping_pairs_offset)
1N/A == 8) && !(le32_to_cpu(
1N/A m->bytes_allocated) -
1N/A le32_to_cpu(m->bytes_in_use))) {
1N/A if (!NInoAttrList(na->ni)) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (ntfs_inode_add_attrlist(
1N/A na->ni))
1N/A return -1;
1N/A goto retry;
1N/A }
1N/A if (ntfs_attr_record_move_away(ctx,
1N/A 8)) {
1N/A ntfs_log_trace("Failed to move "
1N/A "attribute to another "
1N/A "extent. Aborting..\n");
1N/A err = errno;
1N/A goto put_err_out;
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A goto retry;
1N/A }
1N/A if (!(le32_to_cpu(a->length) - le16_to_cpu(
1N/A a->u.nonres.mapping_pairs_offset))) {
1N/A ntfs_log_trace("Size of the space "
1N/A "allocated for mapping "
1N/A "pairs should not be 0."
1N/A " Aborting ...\n");
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A NAttrSetSparse(na);
1N/A a->flags |= ATTR_IS_SPARSE;
1N/A a->u.nonres.compression_unit = 4; /* Windows set it so,
1N/A even if attribute
1N/A is not actually
1N/A compressed. */
1N/A memmove((u8*)a + le16_to_cpu(a->name_offset) +
1N/A 8, (u8*)a + le16_to_cpu(a->name_offset),
1N/A a->name_length * sizeof(ntfschar));
1N/A a->name_offset = cpu_to_le16(le16_to_cpu(
1N/A a->name_offset) + 8);
1N/A a->u.nonres.mapping_pairs_offset =
1N/A cpu_to_le16(le16_to_cpu(
1N/A a->u.nonres.mapping_pairs_offset) + 8);
1N/A /*
1N/A * We should update all mapping pairs, because
1N/A * we shifted their starting position.
1N/A */
1N/A from_vcn = 0;
1N/A }
1N/A /* Attribute becomes normal. */
1N/A if (!sparse && (a->flags & ATTR_IS_SPARSE) &&
1N/A !(a->flags & ATTR_IS_COMPRESSED)) {
1N/A NAttrClearSparse(na);
1N/A a->flags &= ~ATTR_IS_SPARSE;
1N/A a->u.nonres.compression_unit = 0;
1N/A memmove((u8*)a + le16_to_cpu(a->name_offset) -
1N/A 8, (u8*)a + le16_to_cpu(a->name_offset),
1N/A a->name_length * sizeof(ntfschar));
1N/A /*
1N/A * Windows defragmentation tool do not update
1N/A * name offset correctly for unnamed
1N/A * attributes, but chkdsk do not like when it
1N/A * negative, so do not change it at all if it
1N/A * would become negative.
1N/A */
1N/A if (le16_to_cpu(a->name_offset) >= 8)
1N/A a->name_offset = cpu_to_le16(
1N/A le16_to_cpu(
1N/A a->name_offset) - 8);
1N/A a->u.nonres.mapping_pairs_offset =
1N/A cpu_to_le16(le16_to_cpu(
1N/A a->u.nonres.mapping_pairs_offset) - 8);
1N/A /*
1N/A * We should update all mapping pairs, because
1N/A * we shifted their starting position.
1N/A */
1N/A from_vcn = 0;
1N/A }
1N/A /* Update compressed size if required. */
1N/A if (sparse || (a->flags & ATTR_IS_COMPRESSED)) {
1N/A s64 new_compr_size;
1N/A
1N/A new_compr_size = ntfs_rl_get_compressed_size(
1N/A na->ni->vol, na->rl);
1N/A if (new_compr_size == -1) {
1N/A err = errno;
1N/A ntfs_log_trace("BUG! Leaving "
1N/A "inconsistent "
1N/A "metadata.\n");
1N/A goto put_err_out;
1N/A }
1N/A na->compressed_size = new_compr_size;
1N/A a->u.nonres.compressed_size = cpu_to_sle64(
1N/A new_compr_size);
1N/A }
1N/A /*
1N/A * Set FILE_NAME dirty flag, to update sparse bit and
1N/A * allocated size in the index.
1N/A */
1N/A if (na->type == AT_DATA && na->name == AT_UNNAMED) {
1N/A if (sparse)
1N/A na->ni->allocated_size =
1N/A na->compressed_size;
1N/A else
1N/A na->ni->allocated_size =
1N/A na->allocated_size;
1N/A NInoFileNameSetDirty(na->ni);
1N/A }
1N/A
1N/A /*
1N/A * We do want to do anything for the first extent in
1N/A * case we are updating mapping pairs not from the
1N/A * begging.
1N/A */
1N/A if (!a->u.nonres.highest_vcn || from_vcn <=
1N/A sle64_to_cpu(a->u.nonres.highest_vcn) + 1)
1N/A from_vcn = 0;
1N/A else {
1N/A if (from_vcn)
1N/A continue;
1N/A }
1N/A }
1N/A
1N/A /* Get the size for the rest of mapping pairs array. */
1N/A mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl,
1N/A stop_vcn);
1N/A if (mp_size <= 0) {
1N/A err = errno;
1N/A ntfs_log_trace("Get size for mapping pairs failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A /*
1N/A * Determine maximum possible length of mapping pairs,
1N/A * if we shall *not* expand space for mapping pairs.
1N/A */
1N/A cur_max_mp_size = le32_to_cpu(a->length) -
1N/A le16_to_cpu(a->u.nonres.mapping_pairs_offset);
1N/A /*
1N/A * Determine maximum possible length of mapping pairs in the
1N/A * current mft record, if we shall expand space for mapping
1N/A * pairs.
1N/A */
1N/A exp_max_mp_size = le32_to_cpu(m->bytes_allocated) -
1N/A le32_to_cpu(m->bytes_in_use) + cur_max_mp_size;
1N/A /* Test mapping pairs for fitting in the current mft record. */
1N/A if (mp_size > exp_max_mp_size) {
1N/A /*
1N/A * Mapping pairs of $ATTRIBUTE_LIST attribute must fit
1N/A * in the base mft record. Try to move out other
1N/A * attributes and try again.
1N/A */
1N/A if (na->type == AT_ATTRIBUTE_LIST) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (ntfs_inode_free_space(na->ni, mp_size -
1N/A cur_max_mp_size)) {
1N/A if (errno != ENOSPC)
1N/A return -1;
1N/A ntfs_log_error("Attribute list mapping "
1N/A "pairs size to big, "
1N/A "can't fit them in the "
1N/A "base MFT record. "
1N/A "Defragment volume and "
1N/A "try once again.\n");
1N/A errno = ENOSPC;
1N/A return -1;
1N/A }
1N/A goto retry;
1N/A }
1N/A
1N/A /* Add attribute list if it isn't present, and retry. */
1N/A if (!NInoAttrList(base_ni)) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (ntfs_inode_add_attrlist(base_ni)) {
1N/A ntfs_log_trace("Couldn't add attribute "
1N/A "list.\n");
1N/A return -1;
1N/A }
1N/A goto retry;
1N/A }
1N/A
1N/A /*
1N/A * Set mapping pairs size to maximum possible for this
1N/A * mft record. We shall write the rest of mapping pairs
1N/A * to another MFT records.
1N/A */
1N/A mp_size = exp_max_mp_size;
1N/A }
1N/A
1N/A /* Change space for mapping pairs if we need it. */
1N/A if (((mp_size + 7) & ~7) != cur_max_mp_size) {
1N/A if (ntfs_attr_record_resize(m, a,
1N/A le16_to_cpu(a->u.nonres.mapping_pairs_offset) +
1N/A mp_size)) {
1N/A ntfs_log_error("BUG! Ran out of space in mft "
1N/A "record. Please run chkdsk and "
1N/A "if that doesn't find any "
1N/A "errors please report you saw "
1N/A "this message to %s.\n",
1N/A NTFS_DEV_LIST);
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A
1N/A /* Update lowest vcn. */
1N/A a->u.nonres.lowest_vcn = cpu_to_sle64(stop_vcn);
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A if ((ctx->ntfs_ino->nr_extents == -1 ||
1N/A NInoAttrList(ctx->ntfs_ino)) &&
1N/A ctx->attr->type != AT_ATTRIBUTE_LIST) {
1N/A ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn);
1N/A ntfs_attrlist_mark_dirty(ctx->ntfs_ino);
1N/A }
1N/A
1N/A /*
1N/A * Generate the new mapping pairs array directly into the
1N/A * correct destination, i.e. the attribute record itself.
1N/A */
1N/A if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu(
1N/A a->u.nonres.mapping_pairs_offset), mp_size, na->rl,
1N/A stop_vcn, &stop_vcn))
1N/A finished_build = TRUE;
1N/A if (!finished_build && errno != ENOSPC) {
1N/A err = errno;
1N/A ntfs_log_error("BUG! Mapping pairs build failed. "
1N/A "Please run chkdsk and if that doesn't "
1N/A "find any errors please report you saw "
1N/A "this message to %s.\n", NTFS_DEV_LIST);
1N/A goto put_err_out;
1N/A }
1N/A a->u.nonres.highest_vcn = cpu_to_sle64(stop_vcn - 1);
1N/A }
1N/A /* Check whether error occurred. */
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A /* Sanity check. */
1N/A if (from_vcn) {
1N/A err = ENOMSG;
1N/A ntfs_log_error("Library BUG! @from_vcn is nonzero, please "
1N/A "report to %s.\n", NTFS_DEV_LIST);
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Deallocate not used attribute extents and return with success. */
1N/A if (finished_build) {
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A ntfs_log_trace("Deallocate marked extents.\n");
1N/A while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
1N/A CASE_SENSITIVE, 0, NULL, 0, ctx)) {
1N/A if (sle64_to_cpu(ctx->attr->u.nonres.highest_vcn) !=
1N/A NTFS_VCN_DELETE_MARK)
1N/A continue;
1N/A /* Remove unused attribute record. */
1N/A if (ntfs_attr_record_rm(ctx)) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't remove unused "
1N/A "attribute record.\n");
1N/A goto put_err_out;
1N/A }
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A }
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A ntfs_log_trace("Deallocate done.\n");
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ntfs_log_trace("Done!");
1N/A return 0;
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ctx = NULL;
1N/A
1N/A /* Allocate new MFT records for the rest of mapping pairs. */
1N/A while (1) {
1N/A /* Calculate size of rest mapping pairs. */
1N/A mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol,
1N/A na->rl, stop_vcn);
1N/A if (mp_size <= 0) {
1N/A err = errno;
1N/A ntfs_log_trace("Get size for mapping pairs failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A /* Allocate new mft record. */
1N/A ni = ntfs_mft_record_alloc(na->ni->vol, base_ni);
1N/A if (!ni) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't allocate new MFT record.\n");
1N/A goto put_err_out;
1N/A }
1N/A m = ni->mrec;
1N/A /*
1N/A * If mapping size exceed available space, set them to
1N/A * possible maximum.
1N/A */
1N/A cur_max_mp_size = le32_to_cpu(m->bytes_allocated) -
1N/A le32_to_cpu(m->bytes_in_use) -
1N/A (offsetof(ATTR_RECORD, u.nonres.compressed_size) +
1N/A ((NAttrCompressed(na) || NAttrSparse(na)) ?
1N/A sizeof(a->u.nonres.compressed_size) : 0)) -
1N/A ((sizeof(ntfschar) * na->name_len + 7) & ~7);
1N/A if (mp_size > cur_max_mp_size)
1N/A mp_size = cur_max_mp_size;
1N/A /* Add attribute extent to new record. */
1N/A err = ntfs_non_resident_attr_record_add(ni, na->type,
1N/A na->name, na->name_len, stop_vcn, mp_size, 0);
1N/A if (err == -1) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't add attribute extent into the "
1N/A "MFT record.\n");
1N/A if (ntfs_mft_record_free(na->ni->vol, ni)) {
1N/A ntfs_log_trace("Couldn't free MFT record.\n");
1N/A }
1N/A goto put_err_out;
1N/A }
1N/A a = (ATTR_RECORD*)((u8*)m + err);
1N/A
1N/A err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a +
1N/A le16_to_cpu(a->u.nonres.mapping_pairs_offset), mp_size, na->rl,
1N/A stop_vcn, &stop_vcn);
1N/A if (err < 0 && errno != ENOSPC) {
1N/A err = errno;
1N/A ntfs_log_error("BUG! Mapping pairs build failed. "
1N/A "Please run chkdsk and if that doesn't "
1N/A "find any errors please report you saw "
1N/A "this message to %s.\n", NTFS_DEV_LIST);
1N/A if (ntfs_mft_record_free(na->ni->vol, ni))
1N/A ntfs_log_trace("Couldn't free MFT record.\n");
1N/A goto put_err_out;
1N/A }
1N/A a->u.nonres.highest_vcn = cpu_to_sle64(stop_vcn - 1);
1N/A ntfs_inode_mark_dirty(ni);
1N/A /* All mapping pairs has been written. */
1N/A if (!err)
1N/A break;
1N/A }
1N/A ntfs_log_trace("Done!\n");
1N/A return 0;
1N/Aput_err_out:
1N/A if (ctx)
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A#undef NTFS_VCN_DELETE_MARK
1N/A
1N/A/**
1N/A * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute
1N/A * @na: non-resident ntfs attribute to shrink
1N/A * @newsize: new size (in bytes) to which to shrink the attribute
1N/A *
1N/A * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
1N/A *
1N/A * On success return 0 and on error return -1 with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * ENOMEM - Not enough memory to complete operation.
1N/A * ERANGE - @newsize is not valid for the attribute type of @na.
1N/A */
1N/Astatic int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize)
1N/A{
1N/A ntfs_volume *vol;
1N/A ntfs_attr_search_ctx *ctx;
1N/A VCN first_free_vcn;
1N/A s64 nr_freed_clusters;
1N/A int err;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, newsize %lld.\n",
1N/A (unsigned long long)na->ni->mft_no, na->type,
1N/A (long long)newsize);
1N/A
1N/A vol = na->ni->vol;
1N/A
1N/A /*
1N/A * Check the attribute type and the corresponding minimum size
1N/A * against @newsize and fail if @newsize is too small.
1N/A */
1N/A if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
1N/A if (errno == ERANGE) {
1N/A ntfs_log_trace("Eeek! Size bounds check failed. "
1N/A "Aborting...\n");
1N/A } else if (errno == ENOENT)
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A /* The first cluster outside the new allocation. */
1N/A first_free_vcn = (newsize + vol->cluster_size - 1) >>
1N/A vol->cluster_size_bits;
1N/A /*
1N/A * Compare the new allocation with the old one and only deallocate
1N/A * clusters if there is a change.
1N/A */
1N/A if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) {
1N/A if (ntfs_attr_map_whole_runlist(na)) {
1N/A ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist "
1N/A "failed.\n");
1N/A return -1;
1N/A }
1N/A /* Deallocate all clusters starting with the first free one. */
1N/A nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn,
1N/A -1);
1N/A if (nr_freed_clusters < 0) {
1N/A ntfs_log_trace("Eeek! Freeing of clusters failed. "
1N/A "Aborting...\n");
1N/A return -1;
1N/A }
1N/A
1N/A /* Truncate the runlist itself. */
1N/A if (ntfs_rl_truncate(&na->rl, first_free_vcn)) {
1N/A err = errno;
1N/A /*
1N/A * Failed to truncate the runlist, so just throw it
1N/A * away, it will be mapped afresh on next use.
1N/A */
1N/A free(na->rl);
1N/A na->rl = NULL;
1N/A ntfs_log_trace("Eeek! Run list truncation failed.\n");
1N/A errno = err;
1N/A return -1;
1N/A }
1N/A
1N/A /* Prepare to mapping pairs update. */
1N/A na->allocated_size = first_free_vcn << vol->cluster_size_bits;
1N/A /* Write mapping pairs for new runlist. */
1N/A if (ntfs_attr_update_mapping_pairs(na, first_free_vcn)) {
1N/A ntfs_log_trace("Eeek! Mapping pairs update failed. "
1N/A "Leaving inconsistent metadata. "
1N/A "Run chkdsk.\n");
1N/A return -1;
1N/A }
1N/A }
1N/A
1N/A /* Get the first attribute record. */
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx) {
1N/A ntfs_log_trace("Couldn't get attribute search context.\n");
1N/A return -1;
1N/A }
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A err = errno;
1N/A if (err == ENOENT)
1N/A err = EIO;
1N/A ntfs_log_trace("Eeek! Lookup of first attribute extent failed. "
1N/A "Leaving inconsistent metadata.\n");
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Update data and initialized size. */
1N/A na->data_size = newsize;
1N/A ctx->attr->u.nonres.data_size = cpu_to_sle64(newsize);
1N/A if (newsize < na->initialized_size) {
1N/A na->initialized_size = newsize;
1N/A ctx->attr->u.nonres.initialized_size = cpu_to_sle64(newsize);
1N/A }
1N/A /* Update data size in the index. */
1N/A if (na->type == AT_DATA && na->name == AT_UNNAMED) {
1N/A na->ni->data_size = na->data_size;
1N/A NInoFileNameSetDirty(na->ni);
1N/A }
1N/A
1N/A /* If the attribute now has zero size, make it resident. */
1N/A if (!newsize) {
1N/A if (ntfs_attr_make_resident(na, ctx)) {
1N/A /* If couldn't make resident, just continue. */
1N/A if (errno != EPERM)
1N/A ntfs_log_error("Failed to make attribute "
1N/A "resident. Leaving as is...\n");
1N/A }
1N/A }
1N/A
1N/A /* Set the inode dirty so it is written out later. */
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A /* Done! */
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return 0;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute
1N/A * @na: non-resident ntfs attribute to expand
1N/A * @newsize: new size (in bytes) to which to expand the attribute
1N/A * @sparse: if TRUE then will create hole if possible
1N/A *
1N/A * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes,
1N/A * by allocating new clusters.
1N/A *
1N/A * On success return 0 and on error return -1 with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * ENOMEM - Not enough memory to complete operation.
1N/A * ERANGE - @newsize is not valid for the attribute type of @na.
1N/A * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST.
1N/A */
1N/Astatic int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize,
1N/A BOOL sparse)
1N/A{
1N/A VCN first_free_vcn;
1N/A ntfs_volume *vol;
1N/A ntfs_attr_search_ctx *ctx;
1N/A runlist *rl, *rln;
1N/A s64 org_alloc_size;
1N/A int err;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, new size %lld, "
1N/A "current size %lld.\n",
1N/A (unsigned long long)na->ni->mft_no, na->type,
1N/A (long long)newsize, (long long)na->data_size);
1N/A
1N/A vol = na->ni->vol;
1N/A
1N/A /*
1N/A * Check the attribute type and the corresponding maximum size
1N/A * against @newsize and fail if @newsize is too big.
1N/A */
1N/A if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
1N/A if (errno == ERANGE) {
1N/A ntfs_log_trace("Eeek! Size bounds check failed. "
1N/A "Aborting...\n");
1N/A } else if (errno == ENOENT)
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A /* Save for future use. */
1N/A org_alloc_size = na->allocated_size;
1N/A /* The first cluster outside the new allocation. */
1N/A first_free_vcn = (newsize + vol->cluster_size - 1) >>
1N/A vol->cluster_size_bits;
1N/A /*
1N/A * Compare the new allocation with the old one and only allocate
1N/A * clusters if there is a change.
1N/A */
1N/A if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) {
1N/A /* Map required part of runlist. */
1N/A if (ntfs_attr_map_runlist(na, na->allocated_size >>
1N/A vol->cluster_size_bits)) {
1N/A ntfs_log_error("Failed to map runlist.\n");
1N/A return -1;
1N/A }
1N/A
1N/A /*
1N/A * If we extend $DATA attribute on NTFS 3+ volume, we can add
1N/A * sparse runs instead of real allocation of clusters.
1N/A */
1N/A if (na->type == AT_DATA && vol->major_ver >= 3 && sparse) {
1N/A rl = ntfs_malloc(0x1000);
1N/A if (!rl)
1N/A return -1;
1N/A
1N/A rl[0].vcn = (na->allocated_size >>
1N/A vol->cluster_size_bits);
1N/A rl[0].lcn = LCN_HOLE;
1N/A rl[0].length = first_free_vcn -
1N/A (na->allocated_size >> vol->cluster_size_bits);
1N/A rl[1].vcn = first_free_vcn;
1N/A rl[1].lcn = LCN_ENOENT;
1N/A rl[1].length = 0;
1N/A } else {
1N/A /*
1N/A * Determine first after last LCN of attribute.
1N/A * We will start seek clusters from this LCN to avoid
1N/A * fragmentation. If there are no valid LCNs in the
1N/A * attribute let the cluster allocator choose the
1N/A * starting LCN.
1N/A */
1N/A LCN lcn_seek_from;
1N/A
1N/A lcn_seek_from = -1;
1N/A if (na->rl->length) {
1N/A /* Seek to the last run list element. */
1N/A for (rl = na->rl; (rl + 1)->length; rl++)
1N/A ;
1N/A /*
1N/A * If the last LCN is a hole or similar seek
1N/A * back to last valid LCN.
1N/A */
1N/A while (rl->lcn < 0 && rl != na->rl)
1N/A rl--;
1N/A /*
1N/A * Only set lcn_seek_from it the LCN is valid.
1N/A */
1N/A if (rl->lcn >= 0)
1N/A lcn_seek_from = rl->lcn + rl->length;
1N/A }
1N/A
1N/A rl = ntfs_cluster_alloc(vol, na->allocated_size >>
1N/A vol->cluster_size_bits, first_free_vcn -
1N/A (na->allocated_size >>
1N/A vol->cluster_size_bits), lcn_seek_from,
1N/A DATA_ZONE);
1N/A if (!rl) {
1N/A ntfs_log_trace("Cluster allocation failed.\n");
1N/A return -1;
1N/A }
1N/A }
1N/A
1N/A /* Append new clusters to attribute runlist. */
1N/A rln = ntfs_runlists_merge(na->rl, rl);
1N/A if (!rln) {
1N/A /* Failed, free just allocated clusters. */
1N/A err = errno;
1N/A ntfs_log_trace("Run list merge failed.\n");
1N/A ntfs_cluster_free_from_rl(vol, rl);
1N/A free(rl);
1N/A errno = err;
1N/A return -1;
1N/A }
1N/A na->rl = rln;
1N/A
1N/A /* Prepare to mapping pairs update. */
1N/A na->allocated_size = first_free_vcn << vol->cluster_size_bits;
1N/A /* Write mapping pairs for new runlist. */
1N/A if (ntfs_attr_update_mapping_pairs(na, org_alloc_size >>
1N/A vol->cluster_size_bits)) {
1N/A err = errno;
1N/A ntfs_log_trace("Mapping pairs update failed.\n");
1N/A goto rollback;
1N/A }
1N/A }
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
1N/A if (!ctx) {
1N/A ntfs_log_trace("Failed to get search context.\n");
1N/A if (na->allocated_size == org_alloc_size) {
1N/A return -1;
1N/A }
1N/A err = errno;
1N/A goto rollback;
1N/A }
1N/A
1N/A if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A err = errno;
1N/A ntfs_log_trace("Lookup of first attribute extent failed.\n");
1N/A if (err == ENOENT)
1N/A err = EIO;
1N/A if (na->allocated_size != org_alloc_size) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A goto rollback;
1N/A } else
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Update data size. */
1N/A na->data_size = newsize;
1N/A ctx->attr->u.nonres.data_size = cpu_to_sle64(newsize);
1N/A /* Update data size in the index. */
1N/A if (na->type == AT_DATA && na->name == AT_UNNAMED) {
1N/A na->ni->data_size = na->data_size;
1N/A NInoFileNameSetDirty(na->ni);
1N/A }
1N/A /* Set the inode dirty so it is written out later. */
1N/A ntfs_inode_mark_dirty(ctx->ntfs_ino);
1N/A /* Done! */
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return 0;
1N/Arollback:
1N/A /* Free allocated clusters. */
1N/A if (ntfs_cluster_free(vol, na, org_alloc_size >>
1N/A vol->cluster_size_bits, -1) < 0) {
1N/A ntfs_log_trace("Eeek! Leaking clusters. Run chkdsk!\n");
1N/A err = EIO;
1N/A }
1N/A /* Now, truncate the runlist itself. */
1N/A if (ntfs_rl_truncate(&na->rl, org_alloc_size >>
1N/A vol->cluster_size_bits)) {
1N/A /*
1N/A * Failed to truncate the runlist, so just throw it away, it
1N/A * will be mapped afresh on next use.
1N/A */
1N/A free(na->rl);
1N/A na->rl = NULL;
1N/A ntfs_log_trace("Couldn't truncate runlist. Rollback failed.\n");
1N/A } else {
1N/A /* Prepare to mapping pairs update. */
1N/A na->allocated_size = org_alloc_size;
1N/A /* Restore mapping pairs. */
1N/A if (ntfs_attr_update_mapping_pairs(na, na->allocated_size >>
1N/A vol->cluster_size_bits)) {
1N/A ntfs_log_trace("Failed to restore old mapping pairs. "
1N/A "Rollback failed.\n");
1N/A }
1N/A }
1N/A errno = err;
1N/A return -1;
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A
1N/A/**
1N/A * __ntfs_attr_truncate - resize an ntfs attribute
1N/A * @na: open ntfs attribute to resize
1N/A * @newsize: new size (in bytes) to which to resize the attribute
1N/A * @sparse: if TRUE then will create hole if possible
1N/A *
1N/A * Change the size of an open ntfs attribute @na to @newsize bytes. If the
1N/A * attribute is made bigger and the attribute is resident the newly
1N/A * "allocated" space is cleared and if the attribute is non-resident the
1N/A * newly allocated space is marked as not initialised and no real allocation
1N/A * on disk is performed.
1N/A *
1N/A * On success return 0 and on error return -1 with errno set to the error code.
1N/A * The following error codes are defined:
1N/A * EINVAL - Invalid arguments were passed to the function.
1N/A * EACCES - Attribute is encrypted.
1N/A * ERANGE - @newsize is not valid for the attribute type of @na.
1N/A * ENOSPC - There is no enough space on the volume to allocate
1N/A * new clusters or in base mft to resize $ATTRIBUTE_LIST.
1N/A * EOVERFLOW - Resident attribute can not become non resident and
1N/A * already filled whole MFT record, but had not reached
1N/A * @newsize bytes length.
1N/A * EOPNOTSUPP - The desired resize is not implemented yet.
1N/A */
1N/Aint __ntfs_attr_truncate(ntfs_attr *na, const s64 newsize, BOOL sparse)
1N/A{
1N/A int ret;
1N/A
1N/A if (!na || newsize < 0 ||
1N/A (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) {
1N/A ntfs_log_trace("Invalid arguments passed.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
1N/A long)na->ni->mft_no, na->type);
1N/A
1N/A if (na->data_size == newsize)
1N/A return 0;
1N/A /*
1N/A * Encrypted attributes are not supported. We return access denied,
1N/A * which is what Windows NT4 does, too.
1N/A */
1N/A if (NAttrEncrypted(na)) {
1N/A errno = EACCES;
1N/A ntfs_log_trace("Failed (encrypted).\n");
1N/A return -1;
1N/A }
1N/A /*
1N/A * TODO: Implement making handling of compressed attributes.
1N/A */
1N/A if (NAttrCompressed(na)) {
1N/A errno = EOPNOTSUPP;
1N/A ntfs_log_trace("Failed (compressed).\n");
1N/A return -1;
1N/A }
1N/A if (NAttrNonResident(na)) {
1N/A if (newsize > na->data_size)
1N/A ret = ntfs_non_resident_attr_expand(na, newsize,
1N/A sparse);
1N/A else
1N/A ret = ntfs_non_resident_attr_shrink(na, newsize);
1N/A } else
1N/A ret = ntfs_resident_attr_resize(na, newsize);
1N/A if (!ret)
1N/A ntfs_log_trace("Done!\n");
1N/A else
1N/A ntfs_log_trace("Failed.\n");
1N/A return ret;
1N/A}
1N/A
1N/A
1N/A/**
1N/A * Wrapper around __ntfs_attr_truncate that always tries to creates hole
1N/A */
1N/Aint ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
1N/A{
1N/A return __ntfs_attr_truncate(na, newsize, TRUE);
1N/A}
1N/A
1N/A
1N/A/**
1N/A * ntfs_attr_readall - read the entire data from an ntfs attribute
1N/A * @ni: open ntfs inode in which the ntfs attribute resides
1N/A * @type: attribute type
1N/A * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL
1N/A * @name_len: length of attribute @name in Unicode characters (if @name given)
1N/A * @data_size: if non-NULL then store here the data size
1N/A *
1N/A * This function will read the entire content of an ntfs attribute.
1N/A * If @name is AT_UNNAMED then look specifically for an unnamed attribute.
1N/A * If @name is NULL then the attribute could be either named or not.
1N/A * In both those cases @name_len is not used at all.
1N/A *
1N/A * On success a buffer is allocated with the content of the attribute
1N/A * and which needs to be freed when it's not needed anymore. If the
1N/A * @data_size parameter is non-NULL then the data size is set there.
1N/A *
1N/A * On error NULL is returned with errno set to the error code.
1N/A */
1N/Avoid *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
1N/A ntfschar *name, u32 name_len, s64 *data_size)
1N/A{
1N/A ntfs_attr *na;
1N/A void *data, *ret = NULL;
1N/A s64 size;
1N/A
1N/A na = ntfs_attr_open(ni, type, name, name_len);
1N/A if (!na) {
1N/A ntfs_log_perror("ntfs_attr_open failed");
1N/A return NULL;
1N/A }
1N/A data = ntfs_malloc(na->data_size);
1N/A if (!data)
1N/A goto out;
1N/A
1N/A size = ntfs_attr_pread(na, 0, na->data_size, data);
1N/A if (size != na->data_size) {
1N/A ntfs_log_perror("ntfs_attr_pread failed");
1N/A free(data);
1N/A goto out;
1N/A }
1N/A ret = data;
1N/A if (data_size)
1N/A *data_size = size;
1N/Aout:
1N/A ntfs_attr_close(na);
1N/A return ret;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_attr_exist - FIXME: description
1N/A */
1N/Aint ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name,
1N/A u32 name_len)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A int ret;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx)
1N/A return 0;
1N/A
1N/A ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0,
1N/A ctx);
1N/A
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return !ret;
1N/A}