1N/A/**
1N/A * inode.c - Inode handling code. Part of the Linux-NTFS project.
1N/A *
1N/A * Copyright (c) 2002-2005 Anton Altaparmakov
1N/A * Copyright (c) 2004-2007 Yura Pakhuchiy
1N/A * Copyright (c) 2004-2005 Richard Russon
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_STDLIB_H
1N/A#include <stdlib.h>
1N/A#endif
1N/A#ifdef HAVE_STRING_H
1N/A#include <string.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 "types.h"
1N/A#include "attrib.h"
1N/A#include "inode.h"
1N/A#include "debug.h"
1N/A#include "mft.h"
1N/A#include "attrlist.h"
1N/A#include "runlist.h"
1N/A#include "lcnalloc.h"
1N/A#include "index.h"
1N/A#include "dir.h"
1N/A#include "ntfstime.h"
1N/A#include "logging.h"
1N/A
1N/A/**
1N/A * __ntfs_inode_allocate - Create and initialise an NTFS inode object
1N/A * @vol:
1N/A *
1N/A * Description...
1N/A *
1N/A * Returns:
1N/A */
1N/Astatic ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol)
1N/A{
1N/A ntfs_inode *ni;
1N/A
1N/A ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode));
1N/A if (ni) {
1N/A ni->vol = vol;
1N/A INIT_LIST_HEAD(&ni->attr_cache);
1N/A }
1N/A return ni;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_allocate - Create an NTFS inode object
1N/A * @vol:
1N/A *
1N/A * Description...
1N/A *
1N/A * Returns:
1N/A */
1N/Antfs_inode *ntfs_inode_allocate(ntfs_volume *vol)
1N/A{
1N/A return __ntfs_inode_allocate(vol);
1N/A}
1N/A
1N/A/**
1N/A * __ntfs_inode_release - Destroy an NTFS inode object
1N/A * @ni:
1N/A *
1N/A * Description...
1N/A *
1N/A * Returns:
1N/A */
1N/Astatic int __ntfs_inode_release(ntfs_inode *ni)
1N/A{
1N/A if (NInoDirty(ni))
1N/A ntfs_log_debug("Eeek. Discarding dirty inode!\n");
1N/A if (NInoAttrList(ni) && ni->attr_list)
1N/A free(ni->attr_list);
1N/A free(ni->mrec);
1N/A free(ni);
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * __ntfs_inode_add_to_cache - do not use me! Only for internal library use.
1N/A */
1N/Avoid __ntfs_inode_add_to_cache(ntfs_inode *ni)
1N/A{
1N/A list_add_tail(&ni->list_entry, &ni->vol->inode_cache[
1N/A ni->mft_no & NTFS_INODE_CACHE_SIZE_BITS]);
1N/A ni->nr_references = 1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_open - open an inode ready for access
1N/A * @vol: volume to get the inode from
1N/A * @mref: inode number / mft record number to open
1N/A *
1N/A * Allocate an ntfs_inode structure and initialize it for the given inode
1N/A * specified by @mref. @mref specifies the inode number / mft record to read,
1N/A * including the sequence number, which can be 0 if no sequence number checking
1N/A * is to be performed.
1N/A *
1N/A * Then, allocate a buffer for the mft record, read the mft record from the
1N/A * volume @vol, and attach it to the ntfs_inode structure (->mrec). The
1N/A * mft record is mst deprotected and sanity checked for validity and we abort
1N/A * if deprotection or checks fail.
1N/A *
1N/A * Finally, search for an attribute list attribute in the mft record and if one
1N/A * is found, load the attribute list attribute value and attach it to the
1N/A * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate
1N/A * this.
1N/A *
1N/A * Return a pointer to the ntfs_inode structure on success or NULL on error,
1N/A * with errno set to the error code.
1N/A */
1N/Antfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
1N/A{
1N/A s64 l;
1N/A ntfs_inode *ni;
1N/A ntfs_attr_search_ctx *ctx;
1N/A int err = 0;
1N/A STANDARD_INFORMATION *std_info;
1N/A struct list_head *pos;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref));
1N/A if (!vol) {
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A /* Check cache, maybe this inode already opened? */
1N/A list_for_each(pos, &vol->inode_cache[MREF(mref) &
1N/A NTFS_INODE_CACHE_SIZE_BITS]) {
1N/A ntfs_inode *tmp_ni;
1N/A
1N/A tmp_ni = list_entry(pos, ntfs_inode, list_entry);
1N/A if (tmp_ni->mft_no == MREF(mref)) {
1N/A ntfs_log_trace("Found this inode in cache, increment "
1N/A "reference count and return it.\n");
1N/A tmp_ni->nr_references++;
1N/A return tmp_ni;
1N/A }
1N/A }
1N/A /* Search failed. Properly open inode. */
1N/A ni = __ntfs_inode_allocate(vol);
1N/A if (!ni)
1N/A return NULL;
1N/A if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL))
1N/A goto err_out;
1N/A if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) {
1N/A err = ENOENT;
1N/A goto err_out;
1N/A }
1N/A ni->mft_no = MREF(mref);
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx)
1N/A goto err_out;
1N/A /* Receive some basic information about inode. */
1N/A if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
1N/A 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to receive STANDARD_INFORMATION "
1N/A "attribute.\n");
1N/A goto put_err_out;
1N/A }
1N/A std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A ni->flags = std_info->file_attributes;
1N/A ni->creation_time = ntfs2utc(std_info->creation_time);
1N/A ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time);
1N/A ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time);
1N/A ni->last_access_time = ntfs2utc(std_info->last_access_time);
1N/A /* Set attribute list information. */
1N/A if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,
1N/A ctx)) {
1N/A if (errno != ENOENT)
1N/A goto put_err_out;
1N/A /* Attribute list attribute does not present. */
1N/A goto get_size;
1N/A }
1N/A NInoSetAttrList(ni);
1N/A l = ntfs_get_attribute_value_length(ctx->attr);
1N/A if (!l)
1N/A goto put_err_out;
1N/A if (l > 0x40000) {
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/A ni->attr_list_size = l;
1N/A ni->attr_list = ntfs_malloc(ni->attr_list_size);
1N/A if (!ni->attr_list)
1N/A goto put_err_out;
1N/A l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list);
1N/A if (!l)
1N/A goto put_err_out;
1N/A if (l != ni->attr_list_size) {
1N/A err = EIO;
1N/A goto put_err_out;
1N/A }
1N/Aget_size:
1N/A if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
1N/A if (errno != ENOENT)
1N/A goto put_err_out;
1N/A /* Directory or special file. */
1N/A ni->data_size = ni->allocated_size = 0;
1N/A } else {
1N/A if (ctx->attr->non_resident) {
1N/A ni->data_size = sle64_to_cpu(ctx->attr->u.nonres.data_size);
1N/A if (ctx->attr->flags &
1N/A (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE))
1N/A ni->allocated_size = sle64_to_cpu(
1N/A ctx->attr->u.nonres.compressed_size);
1N/A else
1N/A ni->allocated_size = sle64_to_cpu(
1N/A ctx->attr->u.nonres.allocated_size);
1N/A } else {
1N/A ni->data_size = le32_to_cpu(ctx->attr->u.res.value_length);
1N/A ni->allocated_size = (ni->data_size + 7) & ~7;
1N/A }
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A __ntfs_inode_add_to_cache(ni);
1N/A return ni;
1N/Aput_err_out:
1N/A if (!err)
1N/A err = errno;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/Aerr_out:
1N/A if (!err)
1N/A err = errno;
1N/A __ntfs_inode_release(ni);
1N/A errno = err;
1N/A return NULL;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_close - close an ntfs inode and free all associated memory
1N/A * @ni: ntfs inode to close
1N/A *
1N/A * Make sure the ntfs inode @ni is clean.
1N/A *
1N/A * If the ntfs inode @ni is a base inode, close all associated extent inodes,
1N/A * then deallocate all memory attached to it, and finally free the ntfs inode
1N/A * structure itself.
1N/A *
1N/A * If it is an extent inode, we disconnect it from its base inode before we
1N/A * destroy it.
1N/A *
1N/A * It is OK to pass NULL to this function, it is just noop in this case.
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code. On
1N/A * error, @ni has not been freed. The user should attempt to handle the error
1N/A * and call ntfs_inode_close() again. The following error codes are defined:
1N/A *
1N/A * EBUSY @ni and/or its attribute list runlist is/are dirty and the
1N/A * attempt to write it/them to disk failed.
1N/A * EINVAL @ni is invalid (probably it is an extent inode).
1N/A * EIO I/O error while trying to write inode to disk.
1N/A */
1N/Aint ntfs_inode_close(ntfs_inode *ni)
1N/A{
1N/A if (!ni)
1N/A return 0;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
1N/A
1N/A /* Decrement number of users. If there are left then just return. */
1N/A if (ni->nr_extents != -1) {
1N/A ni->nr_references--;
1N/A if (ni->nr_references) {
1N/A ntfs_log_trace("There are %d more references left to "
1N/A "this inode.\n",
1N/A ni->nr_references);
1N/A return 0;
1N/A } else
1N/A ntfs_log_trace("There are no more references left to "
1N/A "this inode.\n");
1N/A }
1N/A /* Check whether all attributes of this inode are closed. */
1N/A if (!list_empty(&ni->attr_cache))
1N/A ntfs_log_error("%s(): Not all attributes are closed. "
1N/A "We definitely have memory leak. "
1N/A "Continue anyway.\n", "ntfs_inode_close");
1N/A /* If we have dirty metadata, write it out. */
1N/A if (NInoDirty(ni) || NInoAttrListDirty(ni)) {
1N/A if (ntfs_inode_sync(ni)) {
1N/A if (errno != EIO)
1N/A errno = EBUSY;
1N/A return -1;
1N/A }
1N/A }
1N/A /* Is this a base inode with mapped extent inodes? */
1N/A if (ni->nr_extents > 0) {
1N/A while (ni->nr_extents > 0) {
1N/A if (ntfs_inode_close(ni->u.extent_nis[0])) {
1N/A if (errno != EIO)
1N/A errno = EBUSY;
1N/A return -1;
1N/A }
1N/A }
1N/A } else if (ni->nr_extents == -1) {
1N/A ntfs_inode **tmp_nis;
1N/A ntfs_inode *base_ni;
1N/A s32 i;
1N/A
1N/A /*
1N/A * If the inode is an extent inode, disconnect it from the
1N/A * base inode before destroying it.
1N/A */
1N/A base_ni = ni->u.base_ni;
1N/A for (i = 0; i < base_ni->nr_extents; ++i) {
1N/A tmp_nis = base_ni->u.extent_nis;
1N/A if (tmp_nis[i] != ni)
1N/A continue;
1N/A /* Found it. Disconnect. */
1N/A memmove(tmp_nis + i, tmp_nis + i + 1,
1N/A (base_ni->nr_extents - i - 1) *
1N/A sizeof(ntfs_inode *));
1N/A /* Buffer should be for multiple of four extents. */
1N/A if ((--base_ni->nr_extents) & 3) {
1N/A i = -1;
1N/A break;
1N/A }
1N/A /*
1N/A * ElectricFence is unhappy with realloc(x,0) as free(x)
1N/A * thus we explicitly separate these two cases.
1N/A */
1N/A if (base_ni->nr_extents) {
1N/A /* Resize the memory buffer. */
1N/A tmp_nis = realloc(tmp_nis, base_ni->nr_extents *
1N/A sizeof(ntfs_inode *));
1N/A /* Ignore errors, they don't really matter. */
1N/A if (tmp_nis)
1N/A base_ni->u.extent_nis = tmp_nis;
1N/A } else if (tmp_nis)
1N/A free(tmp_nis);
1N/A /* Allow for error checking. */
1N/A i = -1;
1N/A break;
1N/A }
1N/A if (i != -1)
1N/A ntfs_log_debug("Extent inode was not attached to base "
1N/A "inode! Continuing regardless.\n");
1N/A }
1N/A /* Remove inode from the list of opened inodes. */
1N/A if (ni->nr_extents != -1)
1N/A list_del(&ni->list_entry);
1N/A return __ntfs_inode_release(ni);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_extent_inode_open - load an extent inode and attach it to its base
1N/A * @base_ni: base ntfs inode
1N/A * @mref: mft reference of the extent inode to load (in little endian)
1N/A *
1N/A * First check if the extent inode @mref is already attached to the base ntfs
1N/A * inode @base_ni, and if so, return a pointer to the attached extent inode.
1N/A *
1N/A * If the extent inode is not already attached to the base inode, allocate an
1N/A * ntfs_inode structure and initialize it for the given inode @mref. @mref
1N/A * specifies the inode number / mft record to read, including the sequence
1N/A * number, which can be 0 if no sequence number checking is to be performed.
1N/A *
1N/A * Then, allocate a buffer for the mft record, read the mft record from the
1N/A * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec).
1N/A * The mft record is mst deprotected and sanity checked for validity and we
1N/A * abort if deprotection or checks fail.
1N/A *
1N/A * Finally attach the ntfs inode to its base inode @base_ni and return a
1N/A * pointer to the ntfs_inode structure on success or NULL on error, with errno
1N/A * set to the error code.
1N/A *
1N/A * Note, extent inodes are never closed directly. They are automatically
1N/A * disposed off by the closing of the base inode.
1N/A */
1N/Antfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const leMFT_REF mref)
1N/A{
1N/A u64 mft_no = MREF_LE(mref);
1N/A ntfs_inode *ni;
1N/A ntfs_inode **extent_nis;
1N/A int i;
1N/A
1N/A if (!base_ni) {
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A ntfs_log_trace("Opening extent inode 0x%llx "
1N/A "(base MFT record 0x%llx).\n",
1N/A (unsigned long long)mft_no,
1N/A (unsigned long long)base_ni->mft_no);
1N/A /* Is the extent inode already open and attached to the base inode? */
1N/A if (base_ni->nr_extents > 0) {
1N/A extent_nis = base_ni->u.extent_nis;
1N/A for (i = 0; i < base_ni->nr_extents; i++) {
1N/A u16 seq_no;
1N/A
1N/A ni = extent_nis[i];
1N/A if (mft_no != ni->mft_no)
1N/A continue;
1N/A /* Verify the sequence number if given. */
1N/A seq_no = MSEQNO_LE(mref);
1N/A if (seq_no && seq_no != le16_to_cpu(
1N/A ni->mrec->sequence_number)) {
1N/A ntfs_log_debug("Found stale extent mft "
1N/A "reference! Corrupt file "
1N/A "system. Run chkdsk.\n");
1N/A errno = EIO;
1N/A return NULL;
1N/A }
1N/A /* We are done, return the extent inode. */
1N/A return ni;
1N/A }
1N/A }
1N/A /* Wasn't there, we need to load the extent inode. */
1N/A ni = __ntfs_inode_allocate(base_ni->vol);
1N/A if (!ni)
1N/A return NULL;
1N/A if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec,
1N/A NULL))
1N/A goto err_out;
1N/A ni->mft_no = mft_no;
1N/A ni->nr_extents = -1;
1N/A ni->u.base_ni = base_ni;
1N/A /* Attach extent inode to base inode, reallocating memory if needed. */
1N/A if (!(base_ni->nr_extents & 3)) {
1N/A i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
1N/A
1N/A extent_nis = (ntfs_inode**)ntfs_malloc(i);
1N/A if (!extent_nis)
1N/A goto err_out;
1N/A if (base_ni->nr_extents) {
1N/A memcpy(extent_nis, base_ni->u.extent_nis,
1N/A i - 4 * sizeof(ntfs_inode *));
1N/A free(base_ni->u.extent_nis);
1N/A }
1N/A base_ni->u.extent_nis = extent_nis;
1N/A }
1N/A base_ni->u.extent_nis[base_ni->nr_extents++] = ni;
1N/A return ni;
1N/Aerr_out:
1N/A i = errno;
1N/A __ntfs_inode_release(ni);
1N/A errno = i;
1N/A ntfs_log_perror("Failed to open extent inode");
1N/A return NULL;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_attach_all_extents - attach all extents for target inode
1N/A * @ni: opened ntfs inode for which perform attach
1N/A *
1N/A * Return 0 on success and -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_inode_attach_all_extents(ntfs_inode *ni)
1N/A{
1N/A ATTR_LIST_ENTRY *ale;
1N/A u64 prev_attached = 0;
1N/A
1N/A if (!ni) {
1N/A ntfs_log_trace("Invalid arguments.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (ni->nr_extents == -1)
1N/A ni = ni->u.base_ni;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
1N/A
1N/A /* Inode haven't got attribute list, thus nothing to attach. */
1N/A if (!NInoAttrList(ni))
1N/A return 0;
1N/A
1N/A if (!ni->attr_list) {
1N/A ntfs_log_trace("Corrupted in-memory structure.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A /* Walk through attribute list and attach all extents. */
1N/A errno = 0;
1N/A ale = (ATTR_LIST_ENTRY *)ni->attr_list;
1N/A while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
1N/A if (ni->mft_no != MREF_LE(ale->mft_reference) &&
1N/A prev_attached != MREF_LE(ale->mft_reference)) {
1N/A if (!ntfs_extent_inode_open(ni, ale->mft_reference)) {
1N/A ntfs_log_trace("Couldn't attach extent "
1N/A "inode (attr type 0x%x "
1N/A "references to it).\n",
1N/A le32_to_cpu(ale->type));
1N/A return -1;
1N/A }
1N/A prev_attached = MREF_LE(ale->mft_reference);
1N/A }
1N/A ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_sync_standard_information - update standard information attribute
1N/A * @ni: ntfs inode to update standard information
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A */
1N/Astatic int ntfs_inode_sync_standard_information(ntfs_inode *ni)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A STANDARD_INFORMATION *std_info;
1N/A int err;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
1N/A 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to receive STANDARD_INFORMATION "
1N/A "attribute.\n");
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = err;
1N/A return -1;
1N/A }
1N/A std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A std_info->file_attributes = ni->flags;
1N/A std_info->creation_time = utc2ntfs(ni->creation_time);
1N/A std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time);
1N/A std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
1N/A std_info->last_access_time = utc2ntfs(ni->last_access_time);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_sync_file_name - update FILE_NAME attributes
1N/A * @ni: ntfs inode to update FILE_NAME attributes
1N/A *
1N/A * Update all FILE_NAME attributes for inode @ni in the index.
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A */
1N/Astatic int ntfs_inode_sync_file_name(ntfs_inode *ni)
1N/A{
1N/A ntfs_attr_search_ctx *ctx = NULL;
1N/A ntfs_index_context *ictx;
1N/A ntfs_inode *index_ni;
1N/A FILE_NAME_ATTR *fn;
1N/A int err = 0;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to get attribute search context.\n");
1N/A goto err_out;
1N/A }
1N/A /* Walk through all FILE_NAME attributes and update them. */
1N/A while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) {
1N/A fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A if (MREF_LE(fn->parent_directory) == ni->mft_no) {
1N/A /*
1N/A * WARNING: We cheater here and obtain 2 attribute
1N/A * search contexts for one inode (first we obtained
1N/A * above, second will be obtained inside
1N/A * ntfs_index_lookup), it's acceptable for library,
1N/A * but will lock kernel.
1N/A */
1N/A index_ni = ni;
1N/A } else
1N/A index_ni = ntfs_inode_open(ni->vol,
1N/A le64_to_cpu(fn->parent_directory));
1N/A if (!index_ni) {
1N/A if (!err)
1N/A err = errno;
1N/A ntfs_log_trace("Failed to open inode with index.\n");
1N/A continue;
1N/A }
1N/A ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4);
1N/A if (!ictx) {
1N/A if (!err)
1N/A err = errno;
1N/A ntfs_log_trace("Failed to get index context.\n");
1N/A ntfs_inode_close(index_ni);
1N/A continue;
1N/A }
1N/A if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) {
1N/A if (!err) {
1N/A if (errno == ENOENT)
1N/A err = EIO;
1N/A else
1N/A err = errno;
1N/A }
1N/A ntfs_log_trace("Index lookup failed.\n");
1N/A ntfs_index_ctx_put(ictx);
1N/A ntfs_inode_close(index_ni);
1N/A continue;
1N/A }
1N/A /* Update flags and file size. */
1N/A fn = (FILE_NAME_ATTR *)ictx->data;
1N/A fn->file_attributes =
1N/A (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) |
1N/A (ni->flags & FILE_ATTR_VALID_FLAGS);
1N/A fn->allocated_size = cpu_to_sle64(ni->allocated_size);
1N/A fn->data_size = cpu_to_sle64(ni->data_size);
1N/A fn->creation_time = utc2ntfs(ni->creation_time);
1N/A fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
1N/A fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
1N/A fn->last_access_time = utc2ntfs(ni->last_access_time);
1N/A ntfs_index_entry_mark_dirty(ictx);
1N/A ntfs_index_ctx_put(ictx);
1N/A if (ni != index_ni)
1N/A ntfs_inode_close(index_ni);
1N/A }
1N/A /* Check for real error occurred. */
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A goto err_out;
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (err) {
1N/A errno = err;
1N/A return -1;
1N/A }
1N/A return 0;
1N/Aerr_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
1N/A/**
1N/A * ntfs_inode_sync - write the inode (and its dirty extents) to disk
1N/A * @ni: ntfs inode to write
1N/A *
1N/A * Write the inode @ni to disk as well as its dirty extent inodes if such
1N/A * exist and @ni is a base inode. If @ni is an extent inode, only @ni is
1N/A * written completely disregarding its base inode and any other extent inodes.
1N/A *
1N/A * For a base inode with dirty extent inodes if any writes fail for whatever
1N/A * reason, the failing inode is skipped and the sync process is continued. At
1N/A * the end the error condition that brought about the failure is returned. Thus
1N/A * the smallest amount of data loss possible occurs.
1N/A *
1N/A * Return 0 on success or -1 on error 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 * EBUSY - Inode and/or one of its extents is busy, try again later.
1N/A * EIO - I/O error while writing the inode (or one of its extents).
1N/A */
1N/Aint ntfs_inode_sync(ntfs_inode *ni)
1N/A{
1N/A int err = 0;
1N/A
1N/A if (!ni) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
1N/A
1N/A /* Update FILE_NAME's in the index. */
1N/A if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
1N/A NInoFileNameTestAndClearDirty(ni) &&
1N/A ntfs_inode_sync_file_name(ni)) {
1N/A if (!err || errno == EIO) {
1N/A err = errno;
1N/A if (err != EIO)
1N/A err = EBUSY;
1N/A }
1N/A ntfs_log_trace("Failed to sync FILE_NAME attributes.\n");
1N/A NInoFileNameSetDirty(ni);
1N/A }
1N/A
1N/A /* Write out attribute list from cache to disk. */
1N/A if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
1N/A NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) {
1N/A ntfs_attr *na;
1N/A
1N/A na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
1N/A if (!na) {
1N/A if (!err || errno == EIO) {
1N/A err = errno;
1N/A if (err != EIO)
1N/A err = EBUSY;
1N/A ntfs_log_trace("Attribute list sync failed "
1N/A "(open failed).\n");
1N/A }
1N/A NInoAttrListSetDirty(ni);
1N/A } else {
1N/A if (na->data_size == ni->attr_list_size) {
1N/A if (ntfs_attr_pwrite(na, 0, ni->attr_list_size,
1N/A ni->attr_list) !=
1N/A ni->attr_list_size) {
1N/A if (!err || errno == EIO) {
1N/A err = errno;
1N/A if (err != EIO)
1N/A err = EBUSY;
1N/A ntfs_log_trace("Attribute list "
1N/A "sync failed "
1N/A "(write).\n");
1N/A }
1N/A NInoAttrListSetDirty(ni);
1N/A }
1N/A } else {
1N/A err = EIO;
1N/A ntfs_log_trace("Attribute list sync failed "
1N/A "(invalid size).\n");
1N/A NInoAttrListSetDirty(ni);
1N/A }
1N/A ntfs_attr_close(na);
1N/A }
1N/A }
1N/A
1N/A /* Write this inode out to the $MFT (and $MFTMirr if applicable). */
1N/A if (NInoTestAndClearDirty(ni)) {
1N/A /* Update STANDARD_INFORMATION. */
1N/A if ((ni->mrec->flags & MFT_RECORD_IN_USE) &&
1N/A ni->nr_extents != -1 &&
1N/A ntfs_inode_sync_standard_information(ni)) {
1N/A if (!err || errno == EIO) {
1N/A err = errno;
1N/A if (err != EIO)
1N/A err = EBUSY;
1N/A }
1N/A ntfs_log_trace("Failed to sync standard "
1N/A "information.\n");
1N/A }
1N/A /* Write MFT record. */
1N/A if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) {
1N/A if (!err || errno == EIO) {
1N/A err = errno;
1N/A if (err != EIO)
1N/A err = EBUSY;
1N/A }
1N/A NInoSetDirty(ni);
1N/A ntfs_log_trace("Base MFT record sync failed.\n");
1N/A }
1N/A }
1N/A
1N/A /* If this is a base inode with extents write all dirty extents, too. */
1N/A if (ni->nr_extents > 0) {
1N/A s32 i;
1N/A
1N/A for (i = 0; i < ni->nr_extents; ++i) {
1N/A ntfs_inode *eni;
1N/A
1N/A eni = ni->u.extent_nis[i];
1N/A if (NInoTestAndClearDirty(eni)) {
1N/A if (ntfs_mft_record_write(eni->vol, eni->mft_no,
1N/A eni->mrec)) {
1N/A if (!err || errno == EIO) {
1N/A err = errno;
1N/A if (err != EIO)
1N/A err = EBUSY;
1N/A }
1N/A NInoSetDirty(eni);
1N/A ntfs_log_trace("Extent MFT record sync "
1N/A "failed.\n");
1N/A }
1N/A }
1N/A }
1N/A }
1N/A
1N/A if (!err)
1N/A return 0;
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_add_attrlist - add attribute list to inode and fill it
1N/A * @ni: opened ntfs inode to which add attribute list
1N/A *
1N/A * Return 0 on success or -1 on error 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 * EEXIST - Attribute list already exist.
1N/A * EIO - Input/Ouput error occurred.
1N/A * ENOMEM - Not enough memory to perform add.
1N/A */
1N/Aint ntfs_inode_add_attrlist(ntfs_inode *ni)
1N/A{
1N/A int err;
1N/A ntfs_attr_search_ctx *ctx;
1N/A u8 *al, *aln;
1N/A int al_len, al_allocated;
1N/A ATTR_LIST_ENTRY *ale;
1N/A ntfs_attr *na;
1N/A
1N/A if (!ni) {
1N/A ntfs_log_trace("Invalid arguments.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
1N/A
1N/A if (NInoAttrList(ni) || ni->nr_extents) {
1N/A ntfs_log_trace("Inode already has got attribute list.\n");
1N/A errno = EEXIST;
1N/A return -1;
1N/A }
1N/A
1N/A al_allocated = 0x40;
1N/A al_len = 0;
1N/A al = malloc(al_allocated);
1N/A NTFS_ON_DEBUG(memset(al, 0, 0x40)); /* Valgrind. */
1N/A ale = (ATTR_LIST_ENTRY *) al;
1N/A if (!al) {
1N/A ntfs_log_trace("Not enough memory.\n");
1N/A errno = ENOMEM;
1N/A return -1;
1N/A }
1N/A
1N/A /* Form attribute list. */
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't get search context.\n");
1N/A goto err_out;
1N/A }
1N/A /* Walk through all attributes. */
1N/A while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {
1N/A if (ctx->attr->type == AT_ATTRIBUTE_LIST) {
1N/A err = EIO;
1N/A ntfs_log_trace("Attribute list already present.\n");
1N/A goto put_err_out;
1N/A }
1N/A /* Calculate new length of attribute list. */
1N/A al_len += (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
1N/A ctx->attr->name_length + 7) & ~7;
1N/A /* Allocate more memory if needed. */
1N/A while (al_len > al_allocated) {
1N/A al_allocated += 0x40;
1N/A aln = realloc(al, al_allocated);
1N/A NTFS_ON_DEBUG(memset(aln + al_allocated - 0x40, 0,
1N/A 0x40)); /* Valgrind. */
1N/A if (!aln) {
1N/A ntfs_log_trace("Not enough memory.\n");
1N/A err = ENOMEM;
1N/A goto put_err_out;
1N/A }
1N/A ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al));
1N/A al = aln;
1N/A }
1N/A /* Add attribute to attribute list. */
1N/A ale->type = ctx->attr->type;
1N/A ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) +
1N/A sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7);
1N/A ale->name_length = ctx->attr->name_length;
1N/A ale->name_offset = (u8 *)ale->name - (u8 *)ale;
1N/A if (ctx->attr->non_resident)
1N/A ale->lowest_vcn = ctx->attr->u.nonres.lowest_vcn;
1N/A else
1N/A ale->lowest_vcn = 0;
1N/A ale->mft_reference = MK_LE_MREF(ni->mft_no,
1N/A le16_to_cpu(ni->mrec->sequence_number));
1N/A ale->instance = ctx->attr->instance;
1N/A memcpy(ale->name, (u8 *)ctx->attr +
1N/A le16_to_cpu(ctx->attr->name_offset),
1N/A ctx->attr->name_length * sizeof(ntfschar));
1N/A ale = (ATTR_LIST_ENTRY *)(al + al_len);
1N/A }
1N/A /* Check for real 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 /* Deallocate trailing memory. */
1N/A aln = realloc(al, al_len);
1N/A if (!aln) {
1N/A err = errno;
1N/A ntfs_log_trace("realloc() failed.\n");
1N/A goto put_err_out;
1N/A }
1N/A al = aln;
1N/A
1N/A /* Set in-memory attribute list. */
1N/A ni->attr_list = al;
1N/A ni->attr_list_size = al_len;
1N/A NInoSetAttrList(ni);
1N/A NInoAttrListSetDirty(ni);
1N/A
1N/A /* Free space if there is not enough it for $ATTRIBUTE_LIST. */
1N/A if (le32_to_cpu(ni->mrec->bytes_allocated) -
1N/A le32_to_cpu(ni->mrec->bytes_in_use) <
1N/A offsetof(ATTR_RECORD, u.res.resident_end)) {
1N/A if (ntfs_inode_free_space(ni,
1N/A offsetof(ATTR_RECORD, u.res.resident_end))) {
1N/A /* Failed to free space. */
1N/A err = errno;
1N/A ntfs_log_trace("Failed to free space for "
1N/A "$ATTRIBUTE_LIST.\n");
1N/A goto rollback;
1N/A }
1N/A }
1N/A
1N/A /* Add $ATTRIBUTE_LIST to mft record. */
1N/A if (ntfs_resident_attr_record_add(ni,
1N/A AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) {
1N/A err = errno;
1N/A ntfs_log_trace("Couldn't add $ATTRIBUTE_LIST to MFT record.\n");
1N/A goto rollback;
1N/A }
1N/A
1N/A /* Resize it. */
1N/A na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
1N/A if (!na) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to open just added $ATTRIBUTE_LIST.\n");
1N/A goto remove_attrlist_record;
1N/A }
1N/A if (ntfs_attr_truncate(na, al_len)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to resize just added $ATTRIBUTE_LIST.\n");
1N/A ntfs_attr_close(na);
1N/A goto remove_attrlist_record;;
1N/A }
1N/A /* Done! */
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ntfs_attr_close(na);
1N/A return 0;
1N/Aremove_attrlist_record:
1N/A /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */
1N/A ni->attr_list = NULL;
1N/A NInoClearAttrList(ni);
1N/A /* Remove $ATTRIBUTE_LIST record. */
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0,
1N/A CASE_SENSITIVE, 0, NULL, 0, ctx)) {
1N/A if (ntfs_attr_record_rm(ctx))
1N/A ntfs_log_trace("Rollback failed. Failed to remove attribute "
1N/A "list record.\n");
1N/A } else
1N/A ntfs_log_trace("Rollback failed. Couldn't find attribute list "
1N/A "record.\n");
1N/A /* Setup back in-memory runlist. */
1N/A ni->attr_list = al;
1N/A ni->attr_list_size = al_len;
1N/A NInoSetAttrList(ni);
1N/Arollback:
1N/A /*
1N/A * Scan attribute list for attributes that placed not in the base MFT
1N/A * record and move them to it.
1N/A */
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A ale = (ATTR_LIST_ENTRY*)al;
1N/A while ((u8*)ale < al + al_len) {
1N/A if (MREF_LE(ale->mft_reference) != ni->mft_no) {
1N/A if (!ntfs_attr_lookup(ale->type, ale->name,
1N/A ale->name_length,
1N/A CASE_SENSITIVE,
1N/A sle64_to_cpu(ale->lowest_vcn),
1N/A NULL, 0, ctx)) {
1N/A if (ntfs_attr_record_move_to(ctx, ni))
1N/A ntfs_log_trace("Rollback failed. Couldn't "
1N/A "back attribute to base MFT record.\n");
1N/A } else
1N/A ntfs_log_trace("Rollback failed. ntfs_attr_lookup "
1N/A "failed.\n");
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A }
1N/A ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length));
1N/A }
1N/A /* Remove in-memory attribute list. */
1N/A ni->attr_list = NULL;
1N/A ni->attr_list_size = 0;
1N/A NInoClearAttrList(ni);
1N/A NInoAttrListClearDirty(ni);
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/Aerr_out:
1N/A free(al);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_free_space - free space in the MFT record of inode
1N/A * @ni: ntfs inode in which MFT record free space
1N/A * @size: amount of space needed to free
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_inode_free_space(ntfs_inode *ni, int size)
1N/A{
1N/A ntfs_attr_search_ctx *ctx;
1N/A int freed, err;
1N/A
1N/A if (!ni || size < 0) {
1N/A ntfs_log_trace("Invalid arguments.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, size %d.\n",
1N/A (long long) ni->mft_no, size);
1N/A
1N/A freed = (le32_to_cpu(ni->mrec->bytes_allocated) -
1N/A le32_to_cpu(ni->mrec->bytes_in_use));
1N/A
1N/A if (size <= freed)
1N/A return 0;
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx) {
1N/A ntfs_log_trace("Failed to get attribute search context.\n");
1N/A return -1;
1N/A }
1N/A
1N/A /*
1N/A * Chkdsk complain if $STANDARD_INFORMATION is not in the base MFT
1N/A * record. FIXME: I'm not sure in this, need to recheck. For now simply
1N/A * do not move $STANDARD_INFORMATION at all.
1N/A *
1N/A * Also we can't move $ATTRIBUTE_LIST from base MFT_RECORD, so position
1N/A * search context on first attribute after $STANDARD_INFORMATION and
1N/A * $ATTRIBUTE_LIST.
1N/A *
1N/A * Why we reposition instead of simply skip this attributes during
1N/A * enumeration? Because in case we have got only in-memory attribute
1N/A * list ntfs_attr_lookup will fail when it will try to find
1N/A * $ATTRIBUTE_LIST.
1N/A */
1N/A if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL,
1N/A 0, ctx)) {
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 if (ctx->attr->type == AT_END) {
1N/A err = ENOSPC;
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A
1N/A while (1) {
1N/A int record_size;
1N/A
1N/A /*
1N/A * Check whether attribute is from different MFT record. If so,
1N/A * find next, because we don't need such.
1N/A */
1N/A while (ctx->ntfs_ino->mft_no != ni->mft_no) {
1N/A if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A err = errno;
1N/A if (errno != ENOENT) {
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A } else
1N/A err = ENOSPC;
1N/A goto put_err_out;
1N/A }
1N/A }
1N/A
1N/A record_size = le32_to_cpu(ctx->attr->length);
1N/A
1N/A /* Move away attribute. */
1N/A if (ntfs_attr_record_move_away(ctx, 0)) {
1N/A err = errno;
1N/A ntfs_log_trace("Failed to move out attribute.\n");
1N/A break;
1N/A }
1N/A freed += record_size;
1N/A
1N/A /* Check whether we done. */
1N/A if (size <= freed) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return 0;
1N/A }
1N/A
1N/A /*
1N/A * Reposition to first attribute after $STANDARD_INFORMATION and
1N/A * $ATTRIBUTE_LIST (see comments upwards).
1N/A */
1N/A ntfs_attr_reinit_search_ctx(ctx);
1N/A if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0,
1N/A NULL, 0, ctx)) {
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_trace("Attribute lookup failed.\n");
1N/A break;
1N/A }
1N/A if (ctx->attr->type == AT_END) {
1N/A err = ENOSPC;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/Aput_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (err == ENOSPC)
1N/A ntfs_log_trace("No attributes left that can be moved out.\n");
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_update_times - update selected time fields for ntfs inode
1N/A * @ni: ntfs inode for which update time fields
1N/A * @mask: select which time fields should be updated
1N/A *
1N/A * This function updates time fields to current time. Fields to update are
1N/A * selected using @mask (see enum @ntfs_time_update_flags for posssible values).
1N/A */
1N/Avoid ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
1N/A{
1N/A time_t now;
1N/A
1N/A if (!ni) {
1N/A ntfs_log_error("%s(): Invalid arguments.\n", "ntfs_inode_update_times");
1N/A return;
1N/A }
1N/A if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) ||
1N/A NVolReadOnly(ni->vol) || !mask)
1N/A return;
1N/A
1N/A now = time(NULL);
1N/A if (mask & NTFS_UPDATE_ATIME)
1N/A ni->last_access_time = now;
1N/A if (mask & NTFS_UPDATE_MTIME)
1N/A ni->last_data_change_time = now;
1N/A if (mask & NTFS_UPDATE_CTIME)
1N/A ni->last_mft_change_time = now;
1N/A NInoFileNameSetDirty(ni);
1N/A NInoSetDirty(ni);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute
1N/A * @mft_no: mft record number where @attr is present
1N/A * @attr: attribute record used to check for the $Bad attribute
1N/A *
1N/A * Check if the mft record given by @mft_no and @attr contains the bad sector
1N/A * list. Please note that mft record numbers describing $Badclus extent inodes
1N/A * will not match the current $Badclus:$Bad check.
1N/A *
1N/A * On success return 1 if the file is $Badclus:$Bad, otherwise return 0.
1N/A * On error return -1 with errno set to the error code.
1N/A */
1N/Aint ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr)
1N/A{
1N/A int len, ret = 0;
1N/A ntfschar *ustr;
1N/A
1N/A if (!attr) {
1N/A ntfs_log_error("Invalid argument.\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (mft_no != FILE_BadClus)
1N/A return 0;
1N/A
1N/A if (attr->type != AT_DATA)
1N/A return 0;
1N/A
1N/A if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) {
1N/A ntfs_log_perror("Couldn't convert '$Bad' to Unicode");
1N/A return -1;
1N/A }
1N/A
1N/A if (ustr && ntfs_names_are_equal(ustr, len,
1N/A (ntfschar *)((u8 *)attr + le16_to_cpu(
1N/A attr->name_offset)), attr->name_length, 0, NULL, 0))
1N/A ret = 1;
1N/A
1N/A ntfs_ucsfree(ustr);
1N/A
1N/A return ret;
1N/A}