180bca3d3SAxel Dörfler/**
280bca3d3SAxel Dörfler * inode.c - Inode handling code. Originated from the Linux-NTFS project.
380bca3d3SAxel Dörfler *
480bca3d3SAxel Dörfler * Copyright (c) 2002-2005 Anton Altaparmakov
530bc84e9SGerasim Troeglazov * Copyright (c) 2002-2008 Szabolcs Szakacsits
630bc84e9SGerasim Troeglazov * Copyright (c) 2004-2007 Yura Pakhuchiy
780bca3d3SAxel Dörfler * Copyright (c) 2004-2005 Richard Russon
80d2c294fSGerasim Troeglazov * Copyright (c) 2009-2010 Jean-Pierre Andre
980bca3d3SAxel Dörfler *
1080bca3d3SAxel Dörfler * This program/include file is free software; you can redistribute it and/or
1180bca3d3SAxel Dörfler * modify it under the terms of the GNU General Public License as published
1280bca3d3SAxel Dörfler * by the Free Software Foundation; either version 2 of the License, or
1380bca3d3SAxel Dörfler * (at your option) any later version.
1480bca3d3SAxel Dörfler *
1580bca3d3SAxel Dörfler * This program/include file is distributed in the hope that it will be
1680bca3d3SAxel Dörfler * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1780bca3d3SAxel Dörfler * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1880bca3d3SAxel Dörfler * GNU General Public License for more details.
1980bca3d3SAxel Dörfler *
2080bca3d3SAxel Dörfler * You should have received a copy of the GNU General Public License
2180bca3d3SAxel Dörfler * along with this program (in the main directory of the NTFS-3G
2280bca3d3SAxel Dörfler * distribution in the file COPYING); if not, write to the Free Software
2380bca3d3SAxel Dörfler * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2480bca3d3SAxel Dörfler */
2580bca3d3SAxel Dörfler
2680bca3d3SAxel Dörfler#ifdef HAVE_CONFIG_H
2780bca3d3SAxel Dörfler#include "config.h"
2880bca3d3SAxel Dörfler#endif
2980bca3d3SAxel Dörfler
3080bca3d3SAxel Dörfler#ifdef HAVE_STDLIB_H
3180bca3d3SAxel Dörfler#include <stdlib.h>
3280bca3d3SAxel Dörfler#endif
3380bca3d3SAxel Dörfler#ifdef HAVE_STRING_H
3480bca3d3SAxel Dörfler#include <string.h>
3580bca3d3SAxel Dörfler#endif
3680bca3d3SAxel Dörfler#ifdef HAVE_ERRNO_H
3780bca3d3SAxel Dörfler#include <errno.h>
3880bca3d3SAxel Dörfler#endif
3980bca3d3SAxel Dörfler
400d2c294fSGerasim Troeglazov#include "param.h"
4180bca3d3SAxel Dörfler#include "compat.h"
4280bca3d3SAxel Dörfler#include "types.h"
430d2c294fSGerasim Troeglazov#include "volume.h"
440d2c294fSGerasim Troeglazov#include "cache.h"
4580bca3d3SAxel Dörfler#include "inode.h"
460d2c294fSGerasim Troeglazov#include "attrib.h"
4780bca3d3SAxel Dörfler#include "debug.h"
4880bca3d3SAxel Dörfler#include "mft.h"
4980bca3d3SAxel Dörfler#include "attrlist.h"
5080bca3d3SAxel Dörfler#include "runlist.h"
5180bca3d3SAxel Dörfler#include "lcnalloc.h"
5280bca3d3SAxel Dörfler#include "index.h"
5380bca3d3SAxel Dörfler#include "dir.h"
5480bca3d3SAxel Dörfler#include "ntfstime.h"
5580bca3d3SAxel Dörfler#include "logging.h"
5680bca3d3SAxel Dörfler#include "misc.h"
570490778eSAugustin Cavalier#include "xattrs.h"
5880bca3d3SAxel Dörfler
5930bc84e9SGerasim Troeglazovntfs_inode *ntfs_inode_base(ntfs_inode *ni)
6030bc84e9SGerasim Troeglazov{
6130bc84e9SGerasim Troeglazov	if (ni->nr_extents == -1)
6230bc84e9SGerasim Troeglazov		return ni->base_ni;
6330bc84e9SGerasim Troeglazov	return ni;
6430bc84e9SGerasim Troeglazov}
6530bc84e9SGerasim Troeglazov
6680bca3d3SAxel Dörfler/**
6780bca3d3SAxel Dörfler * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty
6880bca3d3SAxel Dörfler * @ni:		ntfs inode to set dirty
6980bca3d3SAxel Dörfler *
7080bca3d3SAxel Dörfler * Set the inode @ni dirty so it is written out later (at the latest at
7180bca3d3SAxel Dörfler * ntfs_inode_close() time). If @ni is an extent inode, set the base inode
7280bca3d3SAxel Dörfler * dirty, too.
7380bca3d3SAxel Dörfler *
7480bca3d3SAxel Dörfler * This function cannot fail.
7580bca3d3SAxel Dörfler */
7680bca3d3SAxel Dörflervoid ntfs_inode_mark_dirty(ntfs_inode *ni)
7780bca3d3SAxel Dörfler{
7880bca3d3SAxel Dörfler	NInoSetDirty(ni);
7980bca3d3SAxel Dörfler	if (ni->nr_extents == -1)
8080bca3d3SAxel Dörfler		NInoSetDirty(ni->base_ni);
8180bca3d3SAxel Dörfler}
8280bca3d3SAxel Dörfler
8380bca3d3SAxel Dörfler/**
8480bca3d3SAxel Dörfler * __ntfs_inode_allocate - Create and initialise an NTFS inode object
8580bca3d3SAxel Dörfler * @vol:
8680bca3d3SAxel Dörfler *
8780bca3d3SAxel Dörfler * Description...
8880bca3d3SAxel Dörfler *
8980bca3d3SAxel Dörfler * Returns:
9080bca3d3SAxel Dörfler */
9180bca3d3SAxel Dörflerstatic ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol)
9280bca3d3SAxel Dörfler{
9380bca3d3SAxel Dörfler	ntfs_inode *ni;
9480bca3d3SAxel Dörfler
9530bc84e9SGerasim Troeglazov	ni = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
9680bca3d3SAxel Dörfler	if (ni)
9780bca3d3SAxel Dörfler		ni->vol = vol;
9880bca3d3SAxel Dörfler	return ni;
9980bca3d3SAxel Dörfler}
10080bca3d3SAxel Dörfler
10180bca3d3SAxel Dörfler/**
10280bca3d3SAxel Dörfler * ntfs_inode_allocate - Create an NTFS inode object
10380bca3d3SAxel Dörfler * @vol:
10480bca3d3SAxel Dörfler *
10580bca3d3SAxel Dörfler * Description...
10680bca3d3SAxel Dörfler *
10780bca3d3SAxel Dörfler * Returns:
10880bca3d3SAxel Dörfler */
10980bca3d3SAxel Dörflerntfs_inode *ntfs_inode_allocate(ntfs_volume *vol)
11080bca3d3SAxel Dörfler{
11180bca3d3SAxel Dörfler	return __ntfs_inode_allocate(vol);
11280bca3d3SAxel Dörfler}
11380bca3d3SAxel Dörfler
11480bca3d3SAxel Dörfler/**
11580bca3d3SAxel Dörfler * __ntfs_inode_release - Destroy an NTFS inode object
11680bca3d3SAxel Dörfler * @ni:
11780bca3d3SAxel Dörfler *
11880bca3d3SAxel Dörfler * Description...
11980bca3d3SAxel Dörfler *
12080bca3d3SAxel Dörfler * Returns:
12180bca3d3SAxel Dörfler */
12262cee9f9SGerasim Troeglazovstatic void __ntfs_inode_release(ntfs_inode *ni)
12380bca3d3SAxel Dörfler{
12480bca3d3SAxel Dörfler	if (NInoDirty(ni))
12562cee9f9SGerasim Troeglazov		ntfs_log_error("Releasing dirty inode %lld!\n",
12662cee9f9SGerasim Troeglazov			       (long long)ni->mft_no);
12780bca3d3SAxel Dörfler	if (NInoAttrList(ni) && ni->attr_list)
12880bca3d3SAxel Dörfler		free(ni->attr_list);
12980bca3d3SAxel Dörfler	free(ni->mrec);
13080bca3d3SAxel Dörfler	free(ni);
13162cee9f9SGerasim Troeglazov	return;
13280bca3d3SAxel Dörfler}
13380bca3d3SAxel Dörfler
13480bca3d3SAxel Dörfler/**
13580bca3d3SAxel Dörfler * ntfs_inode_open - open an inode ready for access
13680bca3d3SAxel Dörfler * @vol:	volume to get the inode from
13780bca3d3SAxel Dörfler * @mref:	inode number / mft record number to open
13880bca3d3SAxel Dörfler *
13980bca3d3SAxel Dörfler * Allocate an ntfs_inode structure and initialize it for the given inode
14080bca3d3SAxel Dörfler * specified by @mref. @mref specifies the inode number / mft record to read,
14180bca3d3SAxel Dörfler * including the sequence number, which can be 0 if no sequence number checking
14280bca3d3SAxel Dörfler * is to be performed.
14380bca3d3SAxel Dörfler *
14480bca3d3SAxel Dörfler * Then, allocate a buffer for the mft record, read the mft record from the
14580bca3d3SAxel Dörfler * volume @vol, and attach it to the ntfs_inode structure (->mrec). The
14680bca3d3SAxel Dörfler * mft record is mst deprotected and sanity checked for validity and we abort
14780bca3d3SAxel Dörfler * if deprotection or checks fail.
14880bca3d3SAxel Dörfler *
14980bca3d3SAxel Dörfler * Finally, search for an attribute list attribute in the mft record and if one
15080bca3d3SAxel Dörfler * is found, load the attribute list attribute value and attach it to the
15180bca3d3SAxel Dörfler * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate
15280bca3d3SAxel Dörfler * this.
15380bca3d3SAxel Dörfler *
15480bca3d3SAxel Dörfler * Return a pointer to the ntfs_inode structure on success or NULL on error,
15580bca3d3SAxel Dörfler * with errno set to the error code.
15680bca3d3SAxel Dörfler */
1570d2c294fSGerasim Troeglazovstatic ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
15880bca3d3SAxel Dörfler{
15980bca3d3SAxel Dörfler	s64 l;
16030bc84e9SGerasim Troeglazov	ntfs_inode *ni = NULL;
16180bca3d3SAxel Dörfler	ntfs_attr_search_ctx *ctx;
16280bca3d3SAxel Dörfler	STANDARD_INFORMATION *std_info;
1630d2c294fSGerasim Troeglazov	le32 lthle;
1640d2c294fSGerasim Troeglazov	int olderrno;
16580bca3d3SAxel Dörfler
16630bc84e9SGerasim Troeglazov	ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref));
16780bca3d3SAxel Dörfler	if (!vol) {
16880bca3d3SAxel Dörfler		errno = EINVAL;
16930bc84e9SGerasim Troeglazov		goto out;
17080bca3d3SAxel Dörfler	}
17180bca3d3SAxel Dörfler	ni = __ntfs_inode_allocate(vol);
17280bca3d3SAxel Dörfler	if (!ni)
17330bc84e9SGerasim Troeglazov		goto out;
17480bca3d3SAxel Dörfler	if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL))
17580bca3d3SAxel Dörfler		goto err_out;
17680bca3d3SAxel Dörfler	if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) {
17730bc84e9SGerasim Troeglazov		errno = ENOENT;
17880bca3d3SAxel Dörfler		goto err_out;
17980bca3d3SAxel Dörfler	}
18080bca3d3SAxel Dörfler	ni->mft_no = MREF(mref);
18180bca3d3SAxel Dörfler	ctx = ntfs_attr_get_search_ctx(ni, NULL);
18280bca3d3SAxel Dörfler	if (!ctx)
18380bca3d3SAxel Dörfler		goto err_out;
18480bca3d3SAxel Dörfler	/* Receive some basic information about inode. */
18580bca3d3SAxel Dörfler	if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
18680bca3d3SAxel Dörfler				0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
1870d2c294fSGerasim Troeglazov		if (!ni->mrec->base_mft_record)
1880d2c294fSGerasim Troeglazov			ntfs_log_perror("No STANDARD_INFORMATION in base record"
1890d2c294fSGerasim Troeglazov					" %lld", (long long)MREF(mref));
19080bca3d3SAxel Dörfler		goto put_err_out;
19180bca3d3SAxel Dörfler	}
19280bca3d3SAxel Dörfler	std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
19380bca3d3SAxel Dörfler			le16_to_cpu(ctx->attr->value_offset));
19480bca3d3SAxel Dörfler	ni->flags = std_info->file_attributes;
1950d2c294fSGerasim Troeglazov	ni->creation_time = std_info->creation_time;
1960d2c294fSGerasim Troeglazov	ni->last_data_change_time = std_info->last_data_change_time;
1970d2c294fSGerasim Troeglazov	ni->last_mft_change_time = std_info->last_mft_change_time;
1980d2c294fSGerasim Troeglazov	ni->last_access_time = std_info->last_access_time;
1990d2c294fSGerasim Troeglazov  		/* JPA insert v3 extensions if present */
2000d2c294fSGerasim Troeglazov                /* length may be seen as 72 (v1.x) or 96 (v3.x) */
2010d2c294fSGerasim Troeglazov	lthle = ctx->attr->length;
2020d2c294fSGerasim Troeglazov	if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) {
2030d2c294fSGerasim Troeglazov		set_nino_flag(ni, v3_Extensions);
2040d2c294fSGerasim Troeglazov		ni->owner_id = std_info->owner_id;
2050d2c294fSGerasim Troeglazov		ni->security_id = std_info->security_id;
2060d2c294fSGerasim Troeglazov		ni->quota_charged = std_info->quota_charged;
2070d2c294fSGerasim Troeglazov		ni->usn = std_info->usn;
2080d2c294fSGerasim Troeglazov	} else {
2090d2c294fSGerasim Troeglazov		clear_nino_flag(ni, v3_Extensions);
2100d2c294fSGerasim Troeglazov		ni->owner_id = const_cpu_to_le32(0);
2110d2c294fSGerasim Troeglazov		ni->security_id = const_cpu_to_le32(0);
2120d2c294fSGerasim Troeglazov	}
21380bca3d3SAxel Dörfler	/* Set attribute list information. */
2140d2c294fSGerasim Troeglazov	olderrno = errno;
2150d2c294fSGerasim Troeglazov	if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0,
2160d2c294fSGerasim Troeglazov			CASE_SENSITIVE, 0, NULL, 0, ctx)) {
21780bca3d3SAxel Dörfler		if (errno != ENOENT)
21880bca3d3SAxel Dörfler			goto put_err_out;
21980bca3d3SAxel Dörfler		/* Attribute list attribute does not present. */
2200d2c294fSGerasim Troeglazov		/* restore previous errno to avoid misinterpretation */
2210d2c294fSGerasim Troeglazov		errno = olderrno;
22280bca3d3SAxel Dörfler		goto get_size;
22380bca3d3SAxel Dörfler	}
22480bca3d3SAxel Dörfler	NInoSetAttrList(ni);
22580bca3d3SAxel Dörfler	l = ntfs_get_attribute_value_length(ctx->attr);
22680bca3d3SAxel Dörfler	if (!l)
22780bca3d3SAxel Dörfler		goto put_err_out;
22880bca3d3SAxel Dörfler	if (l > 0x40000) {
22930bc84e9SGerasim Troeglazov		errno = EIO;
2300d2c294fSGerasim Troeglazov		ntfs_log_perror("Too large attrlist attribute (%lld), inode "
2310d2c294fSGerasim Troeglazov				"%lld", (long long)l, (long long)MREF(mref));
23280bca3d3SAxel Dörfler		goto put_err_out;
23380bca3d3SAxel Dörfler	}
23480bca3d3SAxel Dörfler	ni->attr_list_size = l;
23580bca3d3SAxel Dörfler	ni->attr_list = ntfs_malloc(ni->attr_list_size);
23680bca3d3SAxel Dörfler	if (!ni->attr_list)
23780bca3d3SAxel Dörfler		goto put_err_out;
23880bca3d3SAxel Dörfler	l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list);
23980bca3d3SAxel Dörfler	if (!l)
24080bca3d3SAxel Dörfler		goto put_err_out;
24180bca3d3SAxel Dörfler	if (l != ni->attr_list_size) {
24230bc84e9SGerasim Troeglazov		errno = EIO;
2430d2c294fSGerasim Troeglazov		ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode "
2440d2c294fSGerasim Troeglazov				"%lld", (long long)l, ni->attr_list_size,
2450d2c294fSGerasim Troeglazov				(long long)MREF(mref));
24680bca3d3SAxel Dörfler		goto put_err_out;
24780bca3d3SAxel Dörfler	}
24880bca3d3SAxel Dörflerget_size:
2490d2c294fSGerasim Troeglazov	olderrno = errno;
25080bca3d3SAxel Dörfler	if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
25180bca3d3SAxel Dörfler		if (errno != ENOENT)
25280bca3d3SAxel Dörfler			goto put_err_out;
25380bca3d3SAxel Dörfler		/* Directory or special file. */
2540d2c294fSGerasim Troeglazov		/* restore previous errno to avoid misinterpretation */
2550d2c294fSGerasim Troeglazov		errno = olderrno;
25680bca3d3SAxel Dörfler		ni->data_size = ni->allocated_size = 0;
25780bca3d3SAxel Dörfler	} else {
25880bca3d3SAxel Dörfler		if (ctx->attr->non_resident) {
25980bca3d3SAxel Dörfler			ni->data_size = sle64_to_cpu(ctx->attr->data_size);
26080bca3d3SAxel Dörfler			if (ctx->attr->flags &
26180bca3d3SAxel Dörfler					(ATTR_IS_COMPRESSED | ATTR_IS_SPARSE))
26280bca3d3SAxel Dörfler				ni->allocated_size = sle64_to_cpu(
26380bca3d3SAxel Dörfler						ctx->attr->compressed_size);
26480bca3d3SAxel Dörfler			else
26580bca3d3SAxel Dörfler				ni->allocated_size = sle64_to_cpu(
26680bca3d3SAxel Dörfler						ctx->attr->allocated_size);
26780bca3d3SAxel Dörfler		} else {
26880bca3d3SAxel Dörfler			ni->data_size = le32_to_cpu(ctx->attr->value_length);
26980bca3d3SAxel Dörfler			ni->allocated_size = (ni->data_size + 7) & ~7;
27080bca3d3SAxel Dörfler		}
2710d2c294fSGerasim Troeglazov		set_nino_flag(ni,KnownSize);
27280bca3d3SAxel Dörfler	}
27380bca3d3SAxel Dörfler	ntfs_attr_put_search_ctx(ctx);
27430bc84e9SGerasim Troeglazovout:
27530bc84e9SGerasim Troeglazov	ntfs_log_leave("\n");
27680bca3d3SAxel Dörfler	return ni;
27730bc84e9SGerasim Troeglazov
27880bca3d3SAxel Dörflerput_err_out:
27980bca3d3SAxel Dörfler	ntfs_attr_put_search_ctx(ctx);
28080bca3d3SAxel Dörflererr_out:
28180bca3d3SAxel Dörfler	__ntfs_inode_release(ni);
28230bc84e9SGerasim Troeglazov	ni = NULL;
28330bc84e9SGerasim Troeglazov	goto out;
28480bca3d3SAxel Dörfler}
28580bca3d3SAxel Dörfler
28680bca3d3SAxel Dörfler/**
28780bca3d3SAxel Dörfler * ntfs_inode_close - close an ntfs inode and free all associated memory
28880bca3d3SAxel Dörfler * @ni:		ntfs inode to close
28980bca3d3SAxel Dörfler *
29080bca3d3SAxel Dörfler * Make sure the ntfs inode @ni is clean.
29180bca3d3SAxel Dörfler *
29280bca3d3SAxel Dörfler * If the ntfs inode @ni is a base inode, close all associated extent inodes,
29380bca3d3SAxel Dörfler * then deallocate all memory attached to it, and finally free the ntfs inode
29480bca3d3SAxel Dörfler * structure itself.
29580bca3d3SAxel Dörfler *
29680bca3d3SAxel Dörfler * If it is an extent inode, we disconnect it from its base inode before we
29780bca3d3SAxel Dörfler * destroy it.
29880bca3d3SAxel Dörfler *
29930bc84e9SGerasim Troeglazov * It is OK to pass NULL to this function, it is just noop in this case.
30030bc84e9SGerasim Troeglazov *
30180bca3d3SAxel Dörfler * Return 0 on success or -1 on error with errno set to the error code. On
30280bca3d3SAxel Dörfler * error, @ni has not been freed. The user should attempt to handle the error
30380bca3d3SAxel Dörfler * and call ntfs_inode_close() again. The following error codes are defined:
30480bca3d3SAxel Dörfler *
30580bca3d3SAxel Dörfler *	EBUSY	@ni and/or its attribute list runlist is/are dirty and the
30680bca3d3SAxel Dörfler *		attempt to write it/them to disk failed.
30780bca3d3SAxel Dörfler *	EINVAL	@ni is invalid (probably it is an extent inode).
30880bca3d3SAxel Dörfler *	EIO	I/O error while trying to write inode to disk.
30980bca3d3SAxel Dörfler */
3100d2c294fSGerasim Troeglazov
3110d2c294fSGerasim Troeglazovint ntfs_inode_real_close(ntfs_inode *ni)
31280bca3d3SAxel Dörfler{
31330bc84e9SGerasim Troeglazov	int ret = -1;
31430bc84e9SGerasim Troeglazov
31580bca3d3SAxel Dörfler	if (!ni)
31680bca3d3SAxel Dörfler		return 0;
31780bca3d3SAxel Dörfler
31830bc84e9SGerasim Troeglazov	ntfs_log_enter("Entering for inode %lld\n", (long long)ni->mft_no);
31980bca3d3SAxel Dörfler
32080bca3d3SAxel Dörfler	/* If we have dirty metadata, write it out. */
32180bca3d3SAxel Dörfler	if (NInoDirty(ni) || NInoAttrListDirty(ni)) {
32280bca3d3SAxel Dörfler		if (ntfs_inode_sync(ni)) {
32380bca3d3SAxel Dörfler			if (errno != EIO)
32480bca3d3SAxel Dörfler				errno = EBUSY;
32530bc84e9SGerasim Troeglazov			goto err;
32680bca3d3SAxel Dörfler		}
32780bca3d3SAxel Dörfler	}
32880bca3d3SAxel Dörfler	/* Is this a base inode with mapped extent inodes? */
32980bca3d3SAxel Dörfler	if (ni->nr_extents > 0) {
33080bca3d3SAxel Dörfler		while (ni->nr_extents > 0) {
3310d2c294fSGerasim Troeglazov			if (ntfs_inode_real_close(ni->extent_nis[0])) {
33280bca3d3SAxel Dörfler				if (errno != EIO)
33380bca3d3SAxel Dörfler					errno = EBUSY;
33430bc84e9SGerasim Troeglazov				goto err;
33580bca3d3SAxel Dörfler			}
33680bca3d3SAxel Dörfler		}
33780bca3d3SAxel Dörfler	} else if (ni->nr_extents == -1) {
33880bca3d3SAxel Dörfler		ntfs_inode **tmp_nis;
33980bca3d3SAxel Dörfler		ntfs_inode *base_ni;
34080bca3d3SAxel Dörfler		s32 i;
34180bca3d3SAxel Dörfler
34280bca3d3SAxel Dörfler		/*
34380bca3d3SAxel Dörfler		 * If the inode is an extent inode, disconnect it from the
34480bca3d3SAxel Dörfler		 * base inode before destroying it.
34580bca3d3SAxel Dörfler		 */
34680bca3d3SAxel Dörfler		base_ni = ni->base_ni;
34780bca3d3SAxel Dörfler		for (i = 0; i < base_ni->nr_extents; ++i) {
34880bca3d3SAxel Dörfler			tmp_nis = base_ni->extent_nis;
34980bca3d3SAxel Dörfler			if (tmp_nis[i] != ni)
35080bca3d3SAxel Dörfler				continue;
35180bca3d3SAxel Dörfler			/* Found it. Disconnect. */
35280bca3d3SAxel Dörfler			memmove(tmp_nis + i, tmp_nis + i + 1,
35380bca3d3SAxel Dörfler					(base_ni->nr_extents - i - 1) *
35480bca3d3SAxel Dörfler					sizeof(ntfs_inode *));
35580bca3d3SAxel Dörfler			/* Buffer should be for multiple of four extents. */
35680bca3d3SAxel Dörfler			if ((--base_ni->nr_extents) & 3) {
35780bca3d3SAxel Dörfler				i = -1;
35880bca3d3SAxel Dörfler				break;
35980bca3d3SAxel Dörfler			}
36080bca3d3SAxel Dörfler			/*
36180bca3d3SAxel Dörfler			 * ElectricFence is unhappy with realloc(x,0) as free(x)
36280bca3d3SAxel Dörfler			 * thus we explicitly separate these two cases.
36380bca3d3SAxel Dörfler			 */
36480bca3d3SAxel Dörfler			if (base_ni->nr_extents) {
36580bca3d3SAxel Dörfler				/* Resize the memory buffer. */
36680bca3d3SAxel Dörfler				tmp_nis = realloc(tmp_nis, base_ni->nr_extents *
36780bca3d3SAxel Dörfler						  sizeof(ntfs_inode *));
36880bca3d3SAxel Dörfler				/* Ignore errors, they don't really matter. */
36980bca3d3SAxel Dörfler				if (tmp_nis)
37080bca3d3SAxel Dörfler					base_ni->extent_nis = tmp_nis;
3710d2c294fSGerasim Troeglazov			} else if (tmp_nis) {
37280bca3d3SAxel Dörfler				free(tmp_nis);
3730d2c294fSGerasim Troeglazov				base_ni->extent_nis = (ntfs_inode**)NULL;
3740d2c294fSGerasim Troeglazov			}
37580bca3d3SAxel Dörfler			/* Allow for error checking. */
37680bca3d3SAxel Dörfler			i = -1;
37780bca3d3SAxel Dörfler			break;
37880bca3d3SAxel Dörfler		}
37962cee9f9SGerasim Troeglazov
38062cee9f9SGerasim Troeglazov		/*
38162cee9f9SGerasim Troeglazov		 *  We could successfully sync, so only log this error
38262cee9f9SGerasim Troeglazov		 *  and try to sync other inode extents too.
38362cee9f9SGerasim Troeglazov		 */
38480bca3d3SAxel Dörfler		if (i != -1)
38562cee9f9SGerasim Troeglazov			ntfs_log_error("Extent inode %lld was not found\n",
38662cee9f9SGerasim Troeglazov				       (long long)ni->mft_no);
38780bca3d3SAxel Dörfler	}
38862cee9f9SGerasim Troeglazov
38962cee9f9SGerasim Troeglazov	__ntfs_inode_release(ni);
39030bc84e9SGerasim Troeglazov	ret = 0;
39130bc84e9SGerasim Troeglazoverr:
39230bc84e9SGerasim Troeglazov	ntfs_log_leave("\n");
39330bc84e9SGerasim Troeglazov	return ret;
39480bca3d3SAxel Dörfler}
39580bca3d3SAxel Dörfler
3960d2c294fSGerasim Troeglazov#if CACHE_NIDATA_SIZE
3970d2c294fSGerasim Troeglazov
3980d2c294fSGerasim Troeglazov/*
3990d2c294fSGerasim Troeglazov *		Free an inode structure when there is not more space
4000d2c294fSGerasim Troeglazov *	in the cache
4010d2c294fSGerasim Troeglazov */
4020d2c294fSGerasim Troeglazov
4030d2c294fSGerasim Troeglazovvoid ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached)
4040d2c294fSGerasim Troeglazov{
4050d2c294fSGerasim Troeglazov        ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni);
4060d2c294fSGerasim Troeglazov}
4070d2c294fSGerasim Troeglazov
4080d2c294fSGerasim Troeglazov/*
4090d2c294fSGerasim Troeglazov *		Compute a hash value for an inode entry
4100d2c294fSGerasim Troeglazov */
4110d2c294fSGerasim Troeglazov
4120d2c294fSGerasim Troeglazovint ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item)
4130d2c294fSGerasim Troeglazov{
4140d2c294fSGerasim Troeglazov	return (((const struct CACHED_NIDATA*)item)->inum
4150d2c294fSGerasim Troeglazov			% (2*CACHE_NIDATA_SIZE));
4160d2c294fSGerasim Troeglazov}
4170d2c294fSGerasim Troeglazov
4180d2c294fSGerasim Troeglazov/*
4190d2c294fSGerasim Troeglazov *		inum comparing for entering/fetching from cache
4200d2c294fSGerasim Troeglazov */
4210d2c294fSGerasim Troeglazov
4220d2c294fSGerasim Troeglazovstatic int idata_cache_compare(const struct CACHED_GENERIC *cached,
4230d2c294fSGerasim Troeglazov			const struct CACHED_GENERIC *wanted)
4240d2c294fSGerasim Troeglazov{
4250d2c294fSGerasim Troeglazov	return (((const struct CACHED_NIDATA*)cached)->inum
4260d2c294fSGerasim Troeglazov			!= ((const struct CACHED_NIDATA*)wanted)->inum);
4270d2c294fSGerasim Troeglazov}
4280d2c294fSGerasim Troeglazov
4290d2c294fSGerasim Troeglazov/*
4300d2c294fSGerasim Troeglazov *		Invalidate an inode entry when not needed anymore.
4310d2c294fSGerasim Troeglazov *	The entry should have been synced, it may be reused later,
4320d2c294fSGerasim Troeglazov *	if it is requested before it is dropped from cache.
4330d2c294fSGerasim Troeglazov */
4340d2c294fSGerasim Troeglazov
4350d2c294fSGerasim Troeglazovvoid ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref)
4360d2c294fSGerasim Troeglazov{
4370d2c294fSGerasim Troeglazov	struct CACHED_NIDATA item;
4380d2c294fSGerasim Troeglazov
4390d2c294fSGerasim Troeglazov	item.inum = MREF(mref);
4400d2c294fSGerasim Troeglazov	item.ni = (ntfs_inode*)NULL;
4410d2c294fSGerasim Troeglazov	item.pathname = (const char*)NULL;
4420d2c294fSGerasim Troeglazov	item.varsize = 0;
443a814d850Sthreedeyes	ntfs_invalidate_cache(vol->nidata_cache,
4440d2c294fSGerasim Troeglazov				GENERIC(&item),idata_cache_compare,CACHE_FREE);
4450d2c294fSGerasim Troeglazov}
4460d2c294fSGerasim Troeglazov
4470d2c294fSGerasim Troeglazov#endif
4480d2c294fSGerasim Troeglazov
4490d2c294fSGerasim Troeglazov/*
4500d2c294fSGerasim Troeglazov *		Open an inode
4510d2c294fSGerasim Troeglazov *
4520d2c294fSGerasim Troeglazov *	When possible, an entry recorded in the cache is reused
4530d2c294fSGerasim Troeglazov *
4540d2c294fSGerasim Troeglazov *	**NEVER REOPEN** an inode, this can lead to a duplicated
4550d2c294fSGerasim Troeglazov * 	cache entry (hard to detect), and to an obsolete one being
4560d2c294fSGerasim Troeglazov *	reused. System files are however protected from being cached.
4570d2c294fSGerasim Troeglazov */
4580d2c294fSGerasim Troeglazov
4590d2c294fSGerasim Troeglazovntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
4600d2c294fSGerasim Troeglazov{
4610d2c294fSGerasim Troeglazov	ntfs_inode *ni;
4620d2c294fSGerasim Troeglazov#if CACHE_NIDATA_SIZE
4630d2c294fSGerasim Troeglazov	struct CACHED_NIDATA item;
4640d2c294fSGerasim Troeglazov	struct CACHED_NIDATA *cached;
4650d2c294fSGerasim Troeglazov
4660d2c294fSGerasim Troeglazov		/* fetch idata from cache */
4670d2c294fSGerasim Troeglazov	item.inum = MREF(mref);
4680d2c294fSGerasim Troeglazov	debug_double_inode(item.inum,1);
4690d2c294fSGerasim Troeglazov	item.pathname = (const char*)NULL;
4700d2c294fSGerasim Troeglazov	item.varsize = 0;
4710d2c294fSGerasim Troeglazov	cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache,
4720d2c294fSGerasim Troeglazov				GENERIC(&item),idata_cache_compare);
4730d2c294fSGerasim Troeglazov	if (cached) {
4740d2c294fSGerasim Troeglazov		ni = cached->ni;
4750d2c294fSGerasim Troeglazov		/* do not keep open entries in cache */
4760d2c294fSGerasim Troeglazov		ntfs_remove_cache(vol->nidata_cache,
4770d2c294fSGerasim Troeglazov				(struct CACHED_GENERIC*)cached,0);
4780d2c294fSGerasim Troeglazov	} else {
4790d2c294fSGerasim Troeglazov		ni = ntfs_inode_real_open(vol, mref);
4800d2c294fSGerasim Troeglazov	}
481a814d850Sthreedeyes	if (!ni) {
482a814d850Sthreedeyes		debug_double_inode(item.inum, 0);
483a814d850Sthreedeyes	}
4840d2c294fSGerasim Troeglazov#else
4850d2c294fSGerasim Troeglazov	ni = ntfs_inode_real_open(vol, mref);
4860d2c294fSGerasim Troeglazov#endif
4870d2c294fSGerasim Troeglazov	return (ni);
4880d2c294fSGerasim Troeglazov}
4890d2c294fSGerasim Troeglazov
4900d2c294fSGerasim Troeglazov/*
4910d2c294fSGerasim Troeglazov *		Close an inode entry
4920d2c294fSGerasim Troeglazov *
4930d2c294fSGerasim Troeglazov *	If cacheing is in use, the entry is synced and kept available
4940d2c294fSGerasim Troeglazov *	in cache for further use.
4950d2c294fSGerasim Troeglazov *
4960d2c294fSGerasim Troeglazov *	System files (inode < 16 or having the IS_4 flag) are protected
4970d2c294fSGerasim Troeglazov *	against being cached.
4980d2c294fSGerasim Troeglazov */
4990d2c294fSGerasim Troeglazov
5000d2c294fSGerasim Troeglazovint ntfs_inode_close(ntfs_inode *ni)
5010d2c294fSGerasim Troeglazov{
5020d2c294fSGerasim Troeglazov	int res;
5030d2c294fSGerasim Troeglazov#if CACHE_NIDATA_SIZE
5040d2c294fSGerasim Troeglazov	BOOL dirty;
5050d2c294fSGerasim Troeglazov	struct CACHED_NIDATA item;
5060d2c294fSGerasim Troeglazov
5070d2c294fSGerasim Troeglazov	if (ni) {
5080d2c294fSGerasim Troeglazov		debug_double_inode(ni->mft_no,0);
5090d2c294fSGerasim Troeglazov		/* do not cache system files : could lead to double entries */
5100d2c294fSGerasim Troeglazov		if (ni->vol && ni->vol->nidata_cache
5110d2c294fSGerasim Troeglazov			&& ((ni->mft_no == FILE_root)
5120d2c294fSGerasim Troeglazov			    || ((ni->mft_no >= FILE_first_user)
5130d2c294fSGerasim Troeglazov				&& !(ni->mrec->flags & MFT_RECORD_IS_4)))) {
5140d2c294fSGerasim Troeglazov			/* If we have dirty metadata, write it out. */
5150d2c294fSGerasim Troeglazov			dirty = NInoDirty(ni) || NInoAttrListDirty(ni);
5160d2c294fSGerasim Troeglazov			if (dirty) {
5170d2c294fSGerasim Troeglazov				res = ntfs_inode_sync(ni);
5180d2c294fSGerasim Troeglazov					/* do a real close if sync failed */
5190d2c294fSGerasim Troeglazov				if (res)
5200d2c294fSGerasim Troeglazov					ntfs_inode_real_close(ni);
5210d2c294fSGerasim Troeglazov			} else
5220d2c294fSGerasim Troeglazov				res = 0;
5230d2c294fSGerasim Troeglazov
5240d2c294fSGerasim Troeglazov			if (!res) {
5250d2c294fSGerasim Troeglazov					/* feed idata into cache */
5260d2c294fSGerasim Troeglazov				item.inum = ni->mft_no;
5270d2c294fSGerasim Troeglazov				item.ni = ni;
5280d2c294fSGerasim Troeglazov				item.pathname = (const char*)NULL;
5290d2c294fSGerasim Troeglazov				item.varsize = 0;
5300d2c294fSGerasim Troeglazov				debug_cached_inode(ni);
5310d2c294fSGerasim Troeglazov				ntfs_enter_cache(ni->vol->nidata_cache,
5320d2c294fSGerasim Troeglazov					GENERIC(&item), idata_cache_compare);
5330d2c294fSGerasim Troeglazov			}
5340d2c294fSGerasim Troeglazov		} else {
5350d2c294fSGerasim Troeglazov			/* cache not ready or system file, really close */
5360d2c294fSGerasim Troeglazov			res = ntfs_inode_real_close(ni);
5370d2c294fSGerasim Troeglazov		}
5380d2c294fSGerasim Troeglazov	} else
5390d2c294fSGerasim Troeglazov		res = 0;
5400d2c294fSGerasim Troeglazov#else
5410d2c294fSGerasim Troeglazov	res = ntfs_inode_real_close(ni);
5420d2c294fSGerasim Troeglazov#endif
5430d2c294fSGerasim Troeglazov	return (res);
5440d2c294fSGerasim Troeglazov}
5450d2c294fSGerasim Troeglazov
54680bca3d3SAxel Dörfler/**
54780bca3d3SAxel Dörfler * ntfs_extent_inode_open - load an extent inode and attach it to its base
54880bca3d3SAxel Dörfler * @base_ni:	base ntfs inode
54980bca3d3SAxel Dörfler * @mref:	mft reference of the extent inode to load (in little endian)
55080bca3d3SAxel Dörfler *
55180bca3d3SAxel Dörfler * First check if the extent inode @mref is already attached to the base ntfs
55280bca3d3SAxel Dörfler * inode @base_ni, and if so, return a pointer to the attached extent inode.
55380bca3d3SAxel Dörfler *
55480bca3d3SAxel Dörfler * If the extent inode is not already attached to the base inode, allocate an
55580bca3d3SAxel Dörfler * ntfs_inode structure and initialize it for the given inode @mref. @mref
55680bca3d3SAxel Dörfler * specifies the inode number / mft record to read, including the sequence
55780bca3d3SAxel Dörfler * number, which can be 0 if no sequence number checking is to be performed.
55880bca3d3SAxel Dörfler *
55980bca3d3SAxel Dörfler * Then, allocate a buffer for the mft record, read the mft record from the
56080bca3d3SAxel Dörfler * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec).
56180bca3d3SAxel Dörfler * The mft record is mst deprotected and sanity checked for validity and we
56280bca3d3SAxel Dörfler * abort if deprotection or checks fail.
56380bca3d3SAxel Dörfler *
56480bca3d3SAxel Dörfler * Finally attach the ntfs inode to its base inode @base_ni and return a
56580bca3d3SAxel Dörfler * pointer to the ntfs_inode structure on success or NULL on error, with errno
56680bca3d3SAxel Dörfler * set to the error code.
56780bca3d3SAxel Dörfler *
56880bca3d3SAxel Dörfler * Note, extent inodes are never closed directly. They are automatically
56980bca3d3SAxel Dörfler * disposed off by the closing of the base inode.
57080bca3d3SAxel Dörfler */
5710490778eSAugustin Cavalierntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const leMFT_REF mref)
57280bca3d3SAxel Dörfler{
57380bca3d3SAxel Dörfler	u64 mft_no = MREF_LE(mref);
574a814d850Sthreedeyes	VCN extent_vcn;
575a814d850Sthreedeyes	runlist_element *rl;
576a814d850Sthreedeyes	ntfs_volume *vol;
57730bc84e9SGerasim Troeglazov	ntfs_inode *ni = NULL;
57880bca3d3SAxel Dörfler	ntfs_inode **extent_nis;
57980bca3d3SAxel Dörfler	int i;
58080bca3d3SAxel Dörfler
58180bca3d3SAxel Dörfler	if (!base_ni) {
58280bca3d3SAxel Dörfler		errno = EINVAL;
58330bc84e9SGerasim Troeglazov		ntfs_log_perror("%s", __FUNCTION__);
58480bca3d3SAxel Dörfler		return NULL;
58580bca3d3SAxel Dörfler	}
58630bc84e9SGerasim Troeglazov
58730bc84e9SGerasim Troeglazov	ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n",
58880bca3d3SAxel Dörfler			(unsigned long long)mft_no,
58980bca3d3SAxel Dörfler			(unsigned long long)base_ni->mft_no);
59030bc84e9SGerasim Troeglazov
591a814d850Sthreedeyes	if (!base_ni->mft_no) {
592a814d850Sthreedeyes			/*
593a814d850Sthreedeyes			 * When getting extents of MFT, we must be sure
594a814d850Sthreedeyes			 * they are in the MFT part which has already
595a814d850Sthreedeyes			 * been mapped, otherwise we fall into an endless
596a814d850Sthreedeyes			 * recursion.
597a814d850Sthreedeyes			 * Situations have been met where extents locations
598a814d850Sthreedeyes			 * are described in themselves.
599a814d850Sthreedeyes			 * This is a severe error which chkdsk cannot fix.
600a814d850Sthreedeyes			 */
601a814d850Sthreedeyes		vol = base_ni->vol;
602a814d850Sthreedeyes		extent_vcn = mft_no << vol->mft_record_size_bits
603a814d850Sthreedeyes				>> vol->cluster_size_bits;
604a814d850Sthreedeyes		rl = vol->mft_na->rl;
605a814d850Sthreedeyes		if (rl) {
606a814d850Sthreedeyes			while (rl->length
607a814d850Sthreedeyes			    && ((rl->vcn + rl->length) <= extent_vcn))
608a814d850Sthreedeyes				rl++;
609a814d850Sthreedeyes		}
610a814d850Sthreedeyes		if (!rl || (rl->lcn < 0)) {
611a814d850Sthreedeyes			ntfs_log_error("MFT is corrupt, cannot read"
612a814d850Sthreedeyes				" its unmapped extent record %lld\n",
613a814d850Sthreedeyes					(long long)mft_no);
614a814d850Sthreedeyes			ntfs_log_error("Note : chkdsk cannot fix this,"
615a814d850Sthreedeyes				" try ntfsfix\n");
616a814d850Sthreedeyes			errno = EIO;
617a814d850Sthreedeyes			ni = (ntfs_inode*)NULL;
618a814d850Sthreedeyes			goto out;
619a814d850Sthreedeyes		}
620a814d850Sthreedeyes	}
621a814d850Sthreedeyes
62280bca3d3SAxel Dörfler	/* Is the extent inode already open and attached to the base inode? */
62380bca3d3SAxel Dörfler	if (base_ni->nr_extents > 0) {
62480bca3d3SAxel Dörfler		extent_nis = base_ni->extent_nis;
62580bca3d3SAxel Dörfler		for (i = 0; i < base_ni->nr_extents; i++) {
62680bca3d3SAxel Dörfler			u16 seq_no;
62780bca3d3SAxel Dörfler
62880bca3d3SAxel Dörfler			ni = extent_nis[i];
62980bca3d3SAxel Dörfler			if (mft_no != ni->mft_no)
63080bca3d3SAxel Dörfler				continue;
63180bca3d3SAxel Dörfler			/* Verify the sequence number if given. */
63280bca3d3SAxel Dörfler			seq_no = MSEQNO_LE(mref);
63380bca3d3SAxel Dörfler			if (seq_no && seq_no != le16_to_cpu(
63480bca3d3SAxel Dörfler					ni->mrec->sequence_number)) {
63580bca3d3SAxel Dörfler				errno = EIO;
63630bc84e9SGerasim Troeglazov				ntfs_log_perror("Found stale extent mft "
63730bc84e9SGerasim Troeglazov					"reference mft=%lld",
63830bc84e9SGerasim Troeglazov					(long long)ni->mft_no);
63930bc84e9SGerasim Troeglazov				goto out;
64080bca3d3SAxel Dörfler			}
64130bc84e9SGerasim Troeglazov			goto out;
64280bca3d3SAxel Dörfler		}
64380bca3d3SAxel Dörfler	}
64480bca3d3SAxel Dörfler	/* Wasn't there, we need to load the extent inode. */
64580bca3d3SAxel Dörfler	ni = __ntfs_inode_allocate(base_ni->vol);
64680bca3d3SAxel Dörfler	if (!ni)
64730bc84e9SGerasim Troeglazov		goto out;
64830bc84e9SGerasim Troeglazov	if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec, NULL))
64980bca3d3SAxel Dörfler		goto err_out;
65080bca3d3SAxel Dörfler	ni->mft_no = mft_no;
65180bca3d3SAxel Dörfler	ni->nr_extents = -1;
65280bca3d3SAxel Dörfler	ni->base_ni = base_ni;
65380bca3d3SAxel Dörfler	/* Attach extent inode to base inode, reallocating memory if needed. */
65480bca3d3SAxel Dörfler	if (!(base_ni->nr_extents & 3)) {
65580bca3d3SAxel Dörfler		i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
65680bca3d3SAxel Dörfler
65780bca3d3SAxel Dörfler		extent_nis = ntfs_malloc(i);
65880bca3d3SAxel Dörfler		if (!extent_nis)
65980bca3d3SAxel Dörfler			goto err_out;
66080bca3d3SAxel Dörfler		if (base_ni->nr_extents) {
66180bca3d3SAxel Dörfler			memcpy(extent_nis, base_ni->extent_nis,
66280bca3d3SAxel Dörfler					i - 4 * sizeof(ntfs_inode *));
66380bca3d3SAxel Dörfler			free(base_ni->extent_nis);
66480bca3d3SAxel Dörfler		}
66580bca3d3SAxel Dörfler		base_ni->extent_nis = extent_nis;
66680bca3d3SAxel Dörfler	}
66780bca3d3SAxel Dörfler	base_ni->extent_nis[base_ni->nr_extents++] = ni;
66830bc84e9SGerasim Troeglazovout:
66930bc84e9SGerasim Troeglazov	ntfs_log_leave("\n");
67080bca3d3SAxel Dörfler	return ni;
67180bca3d3SAxel Dörflererr_out:
67280bca3d3SAxel Dörfler	__ntfs_inode_release(ni);
67330bc84e9SGerasim Troeglazov	ni = NULL;
67430bc84e9SGerasim Troeglazov	goto out;
67580bca3d3SAxel Dörfler}
67680bca3d3SAxel Dörfler
67780bca3d3SAxel Dörfler/**
67880bca3d3SAxel Dörfler * ntfs_inode_attach_all_extents - attach all extents for target inode
67980bca3d3SAxel Dörfler * @ni:		opened ntfs inode for which perform attach
68080bca3d3SAxel Dörfler *
68180bca3d3SAxel Dörfler * Return 0 on success and -1 on error with errno set to the error code.
68280bca3d3SAxel Dörfler */
68380bca3d3SAxel Dörflerint ntfs_inode_attach_all_extents(ntfs_inode *ni)
68480bca3d3SAxel Dörfler{
68580bca3d3SAxel Dörfler	ATTR_LIST_ENTRY *ale;
68680bca3d3SAxel Dörfler	u64 prev_attached = 0;
68780bca3d3SAxel Dörfler
68880bca3d3SAxel Dörfler	if (!ni) {
68980bca3d3SAxel Dörfler		ntfs_log_trace("Invalid arguments.\n");
69080bca3d3SAxel Dörfler		errno = EINVAL;
69180bca3d3SAxel Dörfler		return -1;
69280bca3d3SAxel Dörfler	}
69380bca3d3SAxel Dörfler
69480bca3d3SAxel Dörfler	if (ni->nr_extents == -1)
69580bca3d3SAxel Dörfler		ni = ni->base_ni;
69680bca3d3SAxel Dörfler
69780bca3d3SAxel Dörfler	ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
69880bca3d3SAxel Dörfler
69980bca3d3SAxel Dörfler	/* Inode haven't got attribute list, thus nothing to attach. */
70080bca3d3SAxel Dörfler	if (!NInoAttrList(ni))
70180bca3d3SAxel Dörfler		return 0;
70280bca3d3SAxel Dörfler
70380bca3d3SAxel Dörfler	if (!ni->attr_list) {
70480bca3d3SAxel Dörfler		ntfs_log_trace("Corrupt in-memory struct.\n");
70580bca3d3SAxel Dörfler		errno = EINVAL;
70680bca3d3SAxel Dörfler		return -1;
70780bca3d3SAxel Dörfler	}
70880bca3d3SAxel Dörfler
70980bca3d3SAxel Dörfler	/* Walk through attribute list and attach all extents. */
71080bca3d3SAxel Dörfler	errno = 0;
71180bca3d3SAxel Dörfler	ale = (ATTR_LIST_ENTRY *)ni->attr_list;
71280bca3d3SAxel Dörfler	while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
71380bca3d3SAxel Dörfler		if (ni->mft_no != MREF_LE(ale->mft_reference) &&
71480bca3d3SAxel Dörfler				prev_attached != MREF_LE(ale->mft_reference)) {
71530bc84e9SGerasim Troeglazov			if (!ntfs_extent_inode_open(ni, ale->mft_reference)) {
71680bca3d3SAxel Dörfler				ntfs_log_trace("Couldn't attach extent inode.\n");
71780bca3d3SAxel Dörfler				return -1;
71880bca3d3SAxel Dörfler			}
71980bca3d3SAxel Dörfler			prev_attached = MREF_LE(ale->mft_reference);
72080bca3d3SAxel Dörfler		}
72180bca3d3SAxel Dörfler		ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
72280bca3d3SAxel Dörfler	}
72380bca3d3SAxel Dörfler	return 0;
72480bca3d3SAxel Dörfler}
72580bca3d3SAxel Dörfler
72680bca3d3SAxel Dörfler/**
72780bca3d3SAxel Dörfler * ntfs_inode_sync_standard_information - update standard information attribute
72880bca3d3SAxel Dörfler * @ni:		ntfs inode to update standard information
72980bca3d3SAxel Dörfler *
73080bca3d3SAxel Dörfler * Return 0 on success or -1 on error with errno set to the error code.
73180bca3d3SAxel Dörfler */
73280bca3d3SAxel Dörflerstatic int ntfs_inode_sync_standard_information(ntfs_inode *ni)
73380bca3d3SAxel Dörfler{
73480bca3d3SAxel Dörfler	ntfs_attr_search_ctx *ctx;
73580bca3d3SAxel Dörfler	STANDARD_INFORMATION *std_info;
7360d2c294fSGerasim Troeglazov	u32 lth;
7370d2c294fSGerasim Troeglazov	le32 lthle;
73880bca3d3SAxel Dörfler
73930bc84e9SGerasim Troeglazov	ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no);
74080bca3d3SAxel Dörfler
74180bca3d3SAxel Dörfler	ctx = ntfs_attr_get_search_ctx(ni, NULL);
74280bca3d3SAxel Dörfler	if (!ctx)
74380bca3d3SAxel Dörfler		return -1;
74480bca3d3SAxel Dörfler	if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
74530bc84e9SGerasim Troeglazov			     0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
74630bc84e9SGerasim Troeglazov		ntfs_log_perror("Failed to sync standard info (inode %lld)",
74730bc84e9SGerasim Troeglazov				(long long)ni->mft_no);
74880bca3d3SAxel Dörfler		ntfs_attr_put_search_ctx(ctx);
74980bca3d3SAxel Dörfler		return -1;
75080bca3d3SAxel Dörfler	}
75180bca3d3SAxel Dörfler	std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
75280bca3d3SAxel Dörfler			le16_to_cpu(ctx->attr->value_offset));
75380bca3d3SAxel Dörfler	std_info->file_attributes = ni->flags;
7540d2c294fSGerasim Troeglazov	if (!test_nino_flag(ni, TimesSet)) {
7550d2c294fSGerasim Troeglazov		std_info->creation_time = ni->creation_time;
7560d2c294fSGerasim Troeglazov		std_info->last_data_change_time = ni->last_data_change_time;
7570d2c294fSGerasim Troeglazov		std_info->last_mft_change_time = ni->last_mft_change_time;
7580d2c294fSGerasim Troeglazov		std_info->last_access_time = ni->last_access_time;
7590d2c294fSGerasim Troeglazov	}
7600d2c294fSGerasim Troeglazov
7610d2c294fSGerasim Troeglazov		/* JPA update v3.x extensions, ensuring consistency */
7620d2c294fSGerasim Troeglazov
7630d2c294fSGerasim Troeglazov	lthle = ctx->attr->length;
7640d2c294fSGerasim Troeglazov	lth = le32_to_cpu(lthle);
7650d2c294fSGerasim Troeglazov	if (test_nino_flag(ni, v3_Extensions)
7660d2c294fSGerasim Troeglazov	    && (lth <= sizeof(STANDARD_INFORMATION)))
7670d2c294fSGerasim Troeglazov		ntfs_log_error("bad sync of standard information\n");
7680d2c294fSGerasim Troeglazov
7690d2c294fSGerasim Troeglazov	if (lth > sizeof(STANDARD_INFORMATION)) {
7700d2c294fSGerasim Troeglazov		std_info->owner_id = ni->owner_id;
7710d2c294fSGerasim Troeglazov		std_info->security_id = ni->security_id;
7720d2c294fSGerasim Troeglazov		std_info->quota_charged = ni->quota_charged;
7730d2c294fSGerasim Troeglazov		std_info->usn = ni->usn;
7740d2c294fSGerasim Troeglazov	}
77580bca3d3SAxel Dörfler	ntfs_inode_mark_dirty(ctx->ntfs_ino);
77680bca3d3SAxel Dörfler	ntfs_attr_put_search_ctx(ctx);
77780bca3d3SAxel Dörfler	return 0;
77880bca3d3SAxel Dörfler}
77980bca3d3SAxel Dörfler
78080bca3d3SAxel Dörfler/**
78180bca3d3SAxel Dörfler * ntfs_inode_sync_file_name - update FILE_NAME attributes
78280bca3d3SAxel Dörfler * @ni:		ntfs inode to update FILE_NAME attributes
78380bca3d3SAxel Dörfler *
784