18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2002-2007 Anton Altaparmakov
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#ifdef NTFS_RW
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/fs.h>
128c2ecf20Sopenharmony_ci#include <linux/highmem.h>
138c2ecf20Sopenharmony_ci#include <linux/buffer_head.h>
148c2ecf20Sopenharmony_ci#include <linux/bitops.h>
158c2ecf20Sopenharmony_ci#include <linux/log2.h>
168c2ecf20Sopenharmony_ci#include <linux/bio.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "attrib.h"
198c2ecf20Sopenharmony_ci#include "aops.h"
208c2ecf20Sopenharmony_ci#include "debug.h"
218c2ecf20Sopenharmony_ci#include "logfile.h"
228c2ecf20Sopenharmony_ci#include "malloc.h"
238c2ecf20Sopenharmony_ci#include "volume.h"
248c2ecf20Sopenharmony_ci#include "ntfs.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * ntfs_check_restart_page_header - check the page header for consistency
288c2ecf20Sopenharmony_ci * @vi:		$LogFile inode to which the restart page header belongs
298c2ecf20Sopenharmony_ci * @rp:		restart page header to check
308c2ecf20Sopenharmony_ci * @pos:	position in @vi at which the restart page header resides
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Check the restart page header @rp for consistency and return 'true' if it is
338c2ecf20Sopenharmony_ci * consistent and 'false' otherwise.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
368c2ecf20Sopenharmony_ci * require the full restart page.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic bool ntfs_check_restart_page_header(struct inode *vi,
398c2ecf20Sopenharmony_ci		RESTART_PAGE_HEADER *rp, s64 pos)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	u32 logfile_system_page_size, logfile_log_page_size;
428c2ecf20Sopenharmony_ci	u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
438c2ecf20Sopenharmony_ci	bool have_usa = true;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
468c2ecf20Sopenharmony_ci	/*
478c2ecf20Sopenharmony_ci	 * If the system or log page sizes are smaller than the ntfs block size
488c2ecf20Sopenharmony_ci	 * or either is not a power of 2 we cannot handle this log file.
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	logfile_system_page_size = le32_to_cpu(rp->system_page_size);
518c2ecf20Sopenharmony_ci	logfile_log_page_size = le32_to_cpu(rp->log_page_size);
528c2ecf20Sopenharmony_ci	if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
538c2ecf20Sopenharmony_ci			logfile_log_page_size < NTFS_BLOCK_SIZE ||
548c2ecf20Sopenharmony_ci			logfile_system_page_size &
558c2ecf20Sopenharmony_ci			(logfile_system_page_size - 1) ||
568c2ecf20Sopenharmony_ci			!is_power_of_2(logfile_log_page_size)) {
578c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile uses unsupported page size.");
588c2ecf20Sopenharmony_ci		return false;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * We must be either at !pos (1st restart page) or at pos = system page
628c2ecf20Sopenharmony_ci	 * size (2nd restart page).
638c2ecf20Sopenharmony_ci	 */
648c2ecf20Sopenharmony_ci	if (pos && pos != logfile_system_page_size) {
658c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "Found restart area in incorrect "
668c2ecf20Sopenharmony_ci				"position in $LogFile.");
678c2ecf20Sopenharmony_ci		return false;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	/* We only know how to handle version 1.1. */
708c2ecf20Sopenharmony_ci	if (sle16_to_cpu(rp->major_ver) != 1 ||
718c2ecf20Sopenharmony_ci			sle16_to_cpu(rp->minor_ver) != 1) {
728c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile version %i.%i is not "
738c2ecf20Sopenharmony_ci				"supported.  (This driver supports version "
748c2ecf20Sopenharmony_ci				"1.1 only.)", (int)sle16_to_cpu(rp->major_ver),
758c2ecf20Sopenharmony_ci				(int)sle16_to_cpu(rp->minor_ver));
768c2ecf20Sopenharmony_ci		return false;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	/*
798c2ecf20Sopenharmony_ci	 * If chkdsk has been run the restart page may not be protected by an
808c2ecf20Sopenharmony_ci	 * update sequence array.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
838c2ecf20Sopenharmony_ci		have_usa = false;
848c2ecf20Sopenharmony_ci		goto skip_usa_checks;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	/* Verify the size of the update sequence array. */
878c2ecf20Sopenharmony_ci	usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
888c2ecf20Sopenharmony_ci	if (usa_count != le16_to_cpu(rp->usa_count)) {
898c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart page specifies "
908c2ecf20Sopenharmony_ci				"inconsistent update sequence array count.");
918c2ecf20Sopenharmony_ci		return false;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci	/* Verify the position of the update sequence array. */
948c2ecf20Sopenharmony_ci	usa_ofs = le16_to_cpu(rp->usa_ofs);
958c2ecf20Sopenharmony_ci	usa_end = usa_ofs + usa_count * sizeof(u16);
968c2ecf20Sopenharmony_ci	if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
978c2ecf20Sopenharmony_ci			usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
988c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart page specifies "
998c2ecf20Sopenharmony_ci				"inconsistent update sequence array offset.");
1008c2ecf20Sopenharmony_ci		return false;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ciskip_usa_checks:
1038c2ecf20Sopenharmony_ci	/*
1048c2ecf20Sopenharmony_ci	 * Verify the position of the restart area.  It must be:
1058c2ecf20Sopenharmony_ci	 *	- aligned to 8-byte boundary,
1068c2ecf20Sopenharmony_ci	 *	- after the update sequence array, and
1078c2ecf20Sopenharmony_ci	 *	- within the system page size.
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	ra_ofs = le16_to_cpu(rp->restart_area_offset);
1108c2ecf20Sopenharmony_ci	if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
1118c2ecf20Sopenharmony_ci			ra_ofs < sizeof(RESTART_PAGE_HEADER)) ||
1128c2ecf20Sopenharmony_ci			ra_ofs > logfile_system_page_size) {
1138c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart page specifies "
1148c2ecf20Sopenharmony_ci				"inconsistent restart area offset.");
1158c2ecf20Sopenharmony_ci		return false;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
1198c2ecf20Sopenharmony_ci	 * set.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
1228c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart page is not modified "
1238c2ecf20Sopenharmony_ci				"by chkdsk but a chkdsk LSN is specified.");
1248c2ecf20Sopenharmony_ci		return false;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	ntfs_debug("Done.");
1278c2ecf20Sopenharmony_ci	return true;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/**
1318c2ecf20Sopenharmony_ci * ntfs_check_restart_area - check the restart area for consistency
1328c2ecf20Sopenharmony_ci * @vi:		$LogFile inode to which the restart page belongs
1338c2ecf20Sopenharmony_ci * @rp:		restart page whose restart area to check
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * Check the restart area of the restart page @rp for consistency and return
1368c2ecf20Sopenharmony_ci * 'true' if it is consistent and 'false' otherwise.
1378c2ecf20Sopenharmony_ci *
1388c2ecf20Sopenharmony_ci * This function assumes that the restart page header has already been
1398c2ecf20Sopenharmony_ci * consistency checked.
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
1428c2ecf20Sopenharmony_ci * require the full restart page.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistatic bool ntfs_check_restart_area(struct inode *vi, RESTART_PAGE_HEADER *rp)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	u64 file_size;
1478c2ecf20Sopenharmony_ci	RESTART_AREA *ra;
1488c2ecf20Sopenharmony_ci	u16 ra_ofs, ra_len, ca_ofs;
1498c2ecf20Sopenharmony_ci	u8 fs_bits;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
1528c2ecf20Sopenharmony_ci	ra_ofs = le16_to_cpu(rp->restart_area_offset);
1538c2ecf20Sopenharmony_ci	ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
1548c2ecf20Sopenharmony_ci	/*
1558c2ecf20Sopenharmony_ci	 * Everything before ra->file_size must be before the first word
1568c2ecf20Sopenharmony_ci	 * protected by an update sequence number.  This ensures that it is
1578c2ecf20Sopenharmony_ci	 * safe to access ra->client_array_offset.
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	if (ra_ofs + offsetof(RESTART_AREA, file_size) >
1608c2ecf20Sopenharmony_ci			NTFS_BLOCK_SIZE - sizeof(u16)) {
1618c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area specifies "
1628c2ecf20Sopenharmony_ci				"inconsistent file offset.");
1638c2ecf20Sopenharmony_ci		return false;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * Now that we can access ra->client_array_offset, make sure everything
1678c2ecf20Sopenharmony_ci	 * up to the log client array is before the first word protected by an
1688c2ecf20Sopenharmony_ci	 * update sequence number.  This ensures we can access all of the
1698c2ecf20Sopenharmony_ci	 * restart area elements safely.  Also, the client array offset must be
1708c2ecf20Sopenharmony_ci	 * aligned to an 8-byte boundary.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	ca_ofs = le16_to_cpu(ra->client_array_offset);
1738c2ecf20Sopenharmony_ci	if (((ca_ofs + 7) & ~7) != ca_ofs ||
1748c2ecf20Sopenharmony_ci			ra_ofs + ca_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) {
1758c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area specifies "
1768c2ecf20Sopenharmony_ci				"inconsistent client array offset.");
1778c2ecf20Sopenharmony_ci		return false;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	/*
1808c2ecf20Sopenharmony_ci	 * The restart area must end within the system page size both when
1818c2ecf20Sopenharmony_ci	 * calculated manually and as specified by ra->restart_area_length.
1828c2ecf20Sopenharmony_ci	 * Also, the calculated length must not exceed the specified length.
1838c2ecf20Sopenharmony_ci	 */
1848c2ecf20Sopenharmony_ci	ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
1858c2ecf20Sopenharmony_ci			sizeof(LOG_CLIENT_RECORD);
1868c2ecf20Sopenharmony_ci	if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) ||
1878c2ecf20Sopenharmony_ci			ra_ofs + le16_to_cpu(ra->restart_area_length) >
1888c2ecf20Sopenharmony_ci			le32_to_cpu(rp->system_page_size) ||
1898c2ecf20Sopenharmony_ci			ra_len > le16_to_cpu(ra->restart_area_length)) {
1908c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area is out of bounds "
1918c2ecf20Sopenharmony_ci				"of the system page size specified by the "
1928c2ecf20Sopenharmony_ci				"restart page header and/or the specified "
1938c2ecf20Sopenharmony_ci				"restart area length is inconsistent.");
1948c2ecf20Sopenharmony_ci		return false;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	/*
1978c2ecf20Sopenharmony_ci	 * The ra->client_free_list and ra->client_in_use_list must be either
1988c2ecf20Sopenharmony_ci	 * LOGFILE_NO_CLIENT or less than ra->log_clients or they are
1998c2ecf20Sopenharmony_ci	 * overflowing the client array.
2008c2ecf20Sopenharmony_ci	 */
2018c2ecf20Sopenharmony_ci	if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
2028c2ecf20Sopenharmony_ci			le16_to_cpu(ra->client_free_list) >=
2038c2ecf20Sopenharmony_ci			le16_to_cpu(ra->log_clients)) ||
2048c2ecf20Sopenharmony_ci			(ra->client_in_use_list != LOGFILE_NO_CLIENT &&
2058c2ecf20Sopenharmony_ci			le16_to_cpu(ra->client_in_use_list) >=
2068c2ecf20Sopenharmony_ci			le16_to_cpu(ra->log_clients))) {
2078c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area specifies "
2088c2ecf20Sopenharmony_ci				"overflowing client free and/or in use lists.");
2098c2ecf20Sopenharmony_ci		return false;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	/*
2128c2ecf20Sopenharmony_ci	 * Check ra->seq_number_bits against ra->file_size for consistency.
2138c2ecf20Sopenharmony_ci	 * We cannot just use ffs() because the file size is not a power of 2.
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	file_size = (u64)sle64_to_cpu(ra->file_size);
2168c2ecf20Sopenharmony_ci	fs_bits = 0;
2178c2ecf20Sopenharmony_ci	while (file_size) {
2188c2ecf20Sopenharmony_ci		file_size >>= 1;
2198c2ecf20Sopenharmony_ci		fs_bits++;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci	if (le32_to_cpu(ra->seq_number_bits) != 67 - fs_bits) {
2228c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area specifies "
2238c2ecf20Sopenharmony_ci				"inconsistent sequence number bits.");
2248c2ecf20Sopenharmony_ci		return false;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	/* The log record header length must be a multiple of 8. */
2278c2ecf20Sopenharmony_ci	if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
2288c2ecf20Sopenharmony_ci			le16_to_cpu(ra->log_record_header_length)) {
2298c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area specifies "
2308c2ecf20Sopenharmony_ci				"inconsistent log record header length.");
2318c2ecf20Sopenharmony_ci		return false;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	/* Dito for the log page data offset. */
2348c2ecf20Sopenharmony_ci	if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
2358c2ecf20Sopenharmony_ci			le16_to_cpu(ra->log_page_data_offset)) {
2368c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "$LogFile restart area specifies "
2378c2ecf20Sopenharmony_ci				"inconsistent log page data offset.");
2388c2ecf20Sopenharmony_ci		return false;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci	ntfs_debug("Done.");
2418c2ecf20Sopenharmony_ci	return true;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/**
2458c2ecf20Sopenharmony_ci * ntfs_check_log_client_array - check the log client array for consistency
2468c2ecf20Sopenharmony_ci * @vi:		$LogFile inode to which the restart page belongs
2478c2ecf20Sopenharmony_ci * @rp:		restart page whose log client array to check
2488c2ecf20Sopenharmony_ci *
2498c2ecf20Sopenharmony_ci * Check the log client array of the restart page @rp for consistency and
2508c2ecf20Sopenharmony_ci * return 'true' if it is consistent and 'false' otherwise.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * This function assumes that the restart page header and the restart area have
2538c2ecf20Sopenharmony_ci * already been consistency checked.
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
2568c2ecf20Sopenharmony_ci * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
2578c2ecf20Sopenharmony_ci * restart page and the page must be multi sector transfer deprotected.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_cistatic bool ntfs_check_log_client_array(struct inode *vi,
2608c2ecf20Sopenharmony_ci		RESTART_PAGE_HEADER *rp)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	RESTART_AREA *ra;
2638c2ecf20Sopenharmony_ci	LOG_CLIENT_RECORD *ca, *cr;
2648c2ecf20Sopenharmony_ci	u16 nr_clients, idx;
2658c2ecf20Sopenharmony_ci	bool in_free_list, idx_is_first;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
2688c2ecf20Sopenharmony_ci	ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
2698c2ecf20Sopenharmony_ci	ca = (LOG_CLIENT_RECORD*)((u8*)ra +
2708c2ecf20Sopenharmony_ci			le16_to_cpu(ra->client_array_offset));
2718c2ecf20Sopenharmony_ci	/*
2728c2ecf20Sopenharmony_ci	 * Check the ra->client_free_list first and then check the
2738c2ecf20Sopenharmony_ci	 * ra->client_in_use_list.  Check each of the log client records in
2748c2ecf20Sopenharmony_ci	 * each of the lists and check that the array does not overflow the
2758c2ecf20Sopenharmony_ci	 * ra->log_clients value.  Also keep track of the number of records
2768c2ecf20Sopenharmony_ci	 * visited as there cannot be more than ra->log_clients records and
2778c2ecf20Sopenharmony_ci	 * that way we detect eventual loops in within a list.
2788c2ecf20Sopenharmony_ci	 */
2798c2ecf20Sopenharmony_ci	nr_clients = le16_to_cpu(ra->log_clients);
2808c2ecf20Sopenharmony_ci	idx = le16_to_cpu(ra->client_free_list);
2818c2ecf20Sopenharmony_ci	in_free_list = true;
2828c2ecf20Sopenharmony_cicheck_list:
2838c2ecf20Sopenharmony_ci	for (idx_is_first = true; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
2848c2ecf20Sopenharmony_ci			idx = le16_to_cpu(cr->next_client)) {
2858c2ecf20Sopenharmony_ci		if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
2868c2ecf20Sopenharmony_ci			goto err_out;
2878c2ecf20Sopenharmony_ci		/* Set @cr to the current log client record. */
2888c2ecf20Sopenharmony_ci		cr = ca + idx;
2898c2ecf20Sopenharmony_ci		/* The first log client record must not have a prev_client. */
2908c2ecf20Sopenharmony_ci		if (idx_is_first) {
2918c2ecf20Sopenharmony_ci			if (cr->prev_client != LOGFILE_NO_CLIENT)
2928c2ecf20Sopenharmony_ci				goto err_out;
2938c2ecf20Sopenharmony_ci			idx_is_first = false;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	/* Switch to and check the in use list if we just did the free list. */
2978c2ecf20Sopenharmony_ci	if (in_free_list) {
2988c2ecf20Sopenharmony_ci		in_free_list = false;
2998c2ecf20Sopenharmony_ci		idx = le16_to_cpu(ra->client_in_use_list);
3008c2ecf20Sopenharmony_ci		goto check_list;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	ntfs_debug("Done.");
3038c2ecf20Sopenharmony_ci	return true;
3048c2ecf20Sopenharmony_cierr_out:
3058c2ecf20Sopenharmony_ci	ntfs_error(vi->i_sb, "$LogFile log client array is corrupt.");
3068c2ecf20Sopenharmony_ci	return false;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci/**
3108c2ecf20Sopenharmony_ci * ntfs_check_and_load_restart_page - check the restart page for consistency
3118c2ecf20Sopenharmony_ci * @vi:		$LogFile inode to which the restart page belongs
3128c2ecf20Sopenharmony_ci * @rp:		restart page to check
3138c2ecf20Sopenharmony_ci * @pos:	position in @vi at which the restart page resides
3148c2ecf20Sopenharmony_ci * @wrp:	[OUT] copy of the multi sector transfer deprotected restart page
3158c2ecf20Sopenharmony_ci * @lsn:	[OUT] set to the current logfile lsn on success
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci * Check the restart page @rp for consistency and return 0 if it is consistent
3188c2ecf20Sopenharmony_ci * and -errno otherwise.  The restart page may have been modified by chkdsk in
3198c2ecf20Sopenharmony_ci * which case its magic is CHKD instead of RSTR.
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
3228c2ecf20Sopenharmony_ci * require the full restart page.
3238c2ecf20Sopenharmony_ci *
3248c2ecf20Sopenharmony_ci * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
3258c2ecf20Sopenharmony_ci * copy of the complete multi sector transfer deprotected page.  On failure,
3268c2ecf20Sopenharmony_ci * *@wrp is undefined.
3278c2ecf20Sopenharmony_ci *
3288c2ecf20Sopenharmony_ci * Simillarly, if @lsn is not NULL, on success *@lsn will be set to the current
3298c2ecf20Sopenharmony_ci * logfile lsn according to this restart page.  On failure, *@lsn is undefined.
3308c2ecf20Sopenharmony_ci *
3318c2ecf20Sopenharmony_ci * The following error codes are defined:
3328c2ecf20Sopenharmony_ci *	-EINVAL	- The restart page is inconsistent.
3338c2ecf20Sopenharmony_ci *	-ENOMEM	- Not enough memory to load the restart page.
3348c2ecf20Sopenharmony_ci *	-EIO	- Failed to reading from $LogFile.
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_cistatic int ntfs_check_and_load_restart_page(struct inode *vi,
3378c2ecf20Sopenharmony_ci		RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
3388c2ecf20Sopenharmony_ci		LSN *lsn)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	RESTART_AREA *ra;
3418c2ecf20Sopenharmony_ci	RESTART_PAGE_HEADER *trp;
3428c2ecf20Sopenharmony_ci	int size, err;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
3458c2ecf20Sopenharmony_ci	/* Check the restart page header for consistency. */
3468c2ecf20Sopenharmony_ci	if (!ntfs_check_restart_page_header(vi, rp, pos)) {
3478c2ecf20Sopenharmony_ci		/* Error output already done inside the function. */
3488c2ecf20Sopenharmony_ci		return -EINVAL;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	/* Check the restart area for consistency. */
3518c2ecf20Sopenharmony_ci	if (!ntfs_check_restart_area(vi, rp)) {
3528c2ecf20Sopenharmony_ci		/* Error output already done inside the function. */
3538c2ecf20Sopenharmony_ci		return -EINVAL;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
3568c2ecf20Sopenharmony_ci	/*
3578c2ecf20Sopenharmony_ci	 * Allocate a buffer to store the whole restart page so we can multi
3588c2ecf20Sopenharmony_ci	 * sector transfer deprotect it.
3598c2ecf20Sopenharmony_ci	 */
3608c2ecf20Sopenharmony_ci	trp = ntfs_malloc_nofs(le32_to_cpu(rp->system_page_size));
3618c2ecf20Sopenharmony_ci	if (!trp) {
3628c2ecf20Sopenharmony_ci		ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile "
3638c2ecf20Sopenharmony_ci				"restart page buffer.");
3648c2ecf20Sopenharmony_ci		return -ENOMEM;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	/*
3678c2ecf20Sopenharmony_ci	 * Read the whole of the restart page into the buffer.  If it fits
3688c2ecf20Sopenharmony_ci	 * completely inside @rp, just copy it from there.  Otherwise map all
3698c2ecf20Sopenharmony_ci	 * the required pages and copy the data from them.
3708c2ecf20Sopenharmony_ci	 */
3718c2ecf20Sopenharmony_ci	size = PAGE_SIZE - (pos & ~PAGE_MASK);
3728c2ecf20Sopenharmony_ci	if (size >= le32_to_cpu(rp->system_page_size)) {
3738c2ecf20Sopenharmony_ci		memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
3748c2ecf20Sopenharmony_ci	} else {
3758c2ecf20Sopenharmony_ci		pgoff_t idx;
3768c2ecf20Sopenharmony_ci		struct page *page;
3778c2ecf20Sopenharmony_ci		int have_read, to_read;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		/* First copy what we already have in @rp. */
3808c2ecf20Sopenharmony_ci		memcpy(trp, rp, size);
3818c2ecf20Sopenharmony_ci		/* Copy the remaining data one page at a time. */
3828c2ecf20Sopenharmony_ci		have_read = size;
3838c2ecf20Sopenharmony_ci		to_read = le32_to_cpu(rp->system_page_size) - size;
3848c2ecf20Sopenharmony_ci		idx = (pos + size) >> PAGE_SHIFT;
3858c2ecf20Sopenharmony_ci		BUG_ON((pos + size) & ~PAGE_MASK);
3868c2ecf20Sopenharmony_ci		do {
3878c2ecf20Sopenharmony_ci			page = ntfs_map_page(vi->i_mapping, idx);
3888c2ecf20Sopenharmony_ci			if (IS_ERR(page)) {
3898c2ecf20Sopenharmony_ci				ntfs_error(vi->i_sb, "Error mapping $LogFile "
3908c2ecf20Sopenharmony_ci						"page (index %lu).", idx);
3918c2ecf20Sopenharmony_ci				err = PTR_ERR(page);
3928c2ecf20Sopenharmony_ci				if (err != -EIO && err != -ENOMEM)
3938c2ecf20Sopenharmony_ci					err = -EIO;
3948c2ecf20Sopenharmony_ci				goto err_out;
3958c2ecf20Sopenharmony_ci			}
3968c2ecf20Sopenharmony_ci			size = min_t(int, to_read, PAGE_SIZE);
3978c2ecf20Sopenharmony_ci			memcpy((u8*)trp + have_read, page_address(page), size);
3988c2ecf20Sopenharmony_ci			ntfs_unmap_page(page);
3998c2ecf20Sopenharmony_ci			have_read += size;
4008c2ecf20Sopenharmony_ci			to_read -= size;
4018c2ecf20Sopenharmony_ci			idx++;
4028c2ecf20Sopenharmony_ci		} while (to_read > 0);
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci	/*
4058c2ecf20Sopenharmony_ci	 * Perform the multi sector transfer deprotection on the buffer if the
4068c2ecf20Sopenharmony_ci	 * restart page is protected.
4078c2ecf20Sopenharmony_ci	 */
4088c2ecf20Sopenharmony_ci	if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
4098c2ecf20Sopenharmony_ci			&& post_read_mst_fixup((NTFS_RECORD*)trp,
4108c2ecf20Sopenharmony_ci			le32_to_cpu(rp->system_page_size))) {
4118c2ecf20Sopenharmony_ci		/*
4128c2ecf20Sopenharmony_ci		 * A multi sector tranfer error was detected.  We only need to
4138c2ecf20Sopenharmony_ci		 * abort if the restart page contents exceed the multi sector
4148c2ecf20Sopenharmony_ci		 * transfer fixup of the first sector.
4158c2ecf20Sopenharmony_ci		 */
4168c2ecf20Sopenharmony_ci		if (le16_to_cpu(rp->restart_area_offset) +
4178c2ecf20Sopenharmony_ci				le16_to_cpu(ra->restart_area_length) >
4188c2ecf20Sopenharmony_ci				NTFS_BLOCK_SIZE - sizeof(u16)) {
4198c2ecf20Sopenharmony_ci			ntfs_error(vi->i_sb, "Multi sector transfer error "
4208c2ecf20Sopenharmony_ci					"detected in $LogFile restart page.");
4218c2ecf20Sopenharmony_ci			err = -EINVAL;
4228c2ecf20Sopenharmony_ci			goto err_out;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	/*
4268c2ecf20Sopenharmony_ci	 * If the restart page is modified by chkdsk or there are no active
4278c2ecf20Sopenharmony_ci	 * logfile clients, the logfile is consistent.  Otherwise, need to
4288c2ecf20Sopenharmony_ci	 * check the log client records for consistency, too.
4298c2ecf20Sopenharmony_ci	 */
4308c2ecf20Sopenharmony_ci	err = 0;
4318c2ecf20Sopenharmony_ci	if (ntfs_is_rstr_record(rp->magic) &&
4328c2ecf20Sopenharmony_ci			ra->client_in_use_list != LOGFILE_NO_CLIENT) {
4338c2ecf20Sopenharmony_ci		if (!ntfs_check_log_client_array(vi, trp)) {
4348c2ecf20Sopenharmony_ci			err = -EINVAL;
4358c2ecf20Sopenharmony_ci			goto err_out;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci	if (lsn) {
4398c2ecf20Sopenharmony_ci		if (ntfs_is_rstr_record(rp->magic))
4408c2ecf20Sopenharmony_ci			*lsn = sle64_to_cpu(ra->current_lsn);
4418c2ecf20Sopenharmony_ci		else /* if (ntfs_is_chkd_record(rp->magic)) */
4428c2ecf20Sopenharmony_ci			*lsn = sle64_to_cpu(rp->chkdsk_lsn);
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci	ntfs_debug("Done.");
4458c2ecf20Sopenharmony_ci	if (wrp)
4468c2ecf20Sopenharmony_ci		*wrp = trp;
4478c2ecf20Sopenharmony_ci	else {
4488c2ecf20Sopenharmony_cierr_out:
4498c2ecf20Sopenharmony_ci		ntfs_free(trp);
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	return err;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci/**
4558c2ecf20Sopenharmony_ci * ntfs_check_logfile - check the journal for consistency
4568c2ecf20Sopenharmony_ci * @log_vi:	struct inode of loaded journal $LogFile to check
4578c2ecf20Sopenharmony_ci * @rp:		[OUT] on success this is a copy of the current restart page
4588c2ecf20Sopenharmony_ci *
4598c2ecf20Sopenharmony_ci * Check the $LogFile journal for consistency and return 'true' if it is
4608c2ecf20Sopenharmony_ci * consistent and 'false' if not.  On success, the current restart page is
4618c2ecf20Sopenharmony_ci * returned in *@rp.  Caller must call ntfs_free(*@rp) when finished with it.
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * At present we only check the two restart pages and ignore the log record
4648c2ecf20Sopenharmony_ci * pages.
4658c2ecf20Sopenharmony_ci *
4668c2ecf20Sopenharmony_ci * Note that the MstProtected flag is not set on the $LogFile inode and hence
4678c2ecf20Sopenharmony_ci * when reading pages they are not deprotected.  This is because we do not know
4688c2ecf20Sopenharmony_ci * if the $LogFile was created on a system with a different page size to ours
4698c2ecf20Sopenharmony_ci * yet and mst deprotection would fail if our page size is smaller.
4708c2ecf20Sopenharmony_ci */
4718c2ecf20Sopenharmony_cibool ntfs_check_logfile(struct inode *log_vi, RESTART_PAGE_HEADER **rp)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	s64 size, pos;
4748c2ecf20Sopenharmony_ci	LSN rstr1_lsn, rstr2_lsn;
4758c2ecf20Sopenharmony_ci	ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
4768c2ecf20Sopenharmony_ci	struct address_space *mapping = log_vi->i_mapping;
4778c2ecf20Sopenharmony_ci	struct page *page = NULL;
4788c2ecf20Sopenharmony_ci	u8 *kaddr = NULL;
4798c2ecf20Sopenharmony_ci	RESTART_PAGE_HEADER *rstr1_ph = NULL;
4808c2ecf20Sopenharmony_ci	RESTART_PAGE_HEADER *rstr2_ph = NULL;
4818c2ecf20Sopenharmony_ci	int log_page_size, log_page_mask, err;
4828c2ecf20Sopenharmony_ci	bool logfile_is_empty = true;
4838c2ecf20Sopenharmony_ci	u8 log_page_bits;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
4868c2ecf20Sopenharmony_ci	/* An empty $LogFile must have been clean before it got emptied. */
4878c2ecf20Sopenharmony_ci	if (NVolLogFileEmpty(vol))
4888c2ecf20Sopenharmony_ci		goto is_empty;
4898c2ecf20Sopenharmony_ci	size = i_size_read(log_vi);
4908c2ecf20Sopenharmony_ci	/* Make sure the file doesn't exceed the maximum allowed size. */
4918c2ecf20Sopenharmony_ci	if (size > MaxLogFileSize)
4928c2ecf20Sopenharmony_ci		size = MaxLogFileSize;
4938c2ecf20Sopenharmony_ci	/*
4948c2ecf20Sopenharmony_ci	 * Truncate size to a multiple of the page cache size or the default
4958c2ecf20Sopenharmony_ci	 * log page size if the page cache size is between the default log page
4968c2ecf20Sopenharmony_ci	 * log page size if the page cache size is between the default log page
4978c2ecf20Sopenharmony_ci	 * size and twice that.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci	if (PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <=
5008c2ecf20Sopenharmony_ci			DefaultLogPageSize * 2)
5018c2ecf20Sopenharmony_ci		log_page_size = DefaultLogPageSize;
5028c2ecf20Sopenharmony_ci	else
5038c2ecf20Sopenharmony_ci		log_page_size = PAGE_SIZE;
5048c2ecf20Sopenharmony_ci	log_page_mask = log_page_size - 1;
5058c2ecf20Sopenharmony_ci	/*
5068c2ecf20Sopenharmony_ci	 * Use ntfs_ffs() instead of ffs() to enable the compiler to
5078c2ecf20Sopenharmony_ci	 * optimize log_page_size and log_page_bits into constants.
5088c2ecf20Sopenharmony_ci	 */
5098c2ecf20Sopenharmony_ci	log_page_bits = ntfs_ffs(log_page_size) - 1;
5108c2ecf20Sopenharmony_ci	size &= ~(s64)(log_page_size - 1);
5118c2ecf20Sopenharmony_ci	/*
5128c2ecf20Sopenharmony_ci	 * Ensure the log file is big enough to store at least the two restart
5138c2ecf20Sopenharmony_ci	 * pages and the minimum number of log record pages.
5148c2ecf20Sopenharmony_ci	 */
5158c2ecf20Sopenharmony_ci	if (size < log_page_size * 2 || (size - log_page_size * 2) >>
5168c2ecf20Sopenharmony_ci			log_page_bits < MinLogRecordPages) {
5178c2ecf20Sopenharmony_ci		ntfs_error(vol->sb, "$LogFile is too small.");
5188c2ecf20Sopenharmony_ci		return false;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	/*
5218c2ecf20Sopenharmony_ci	 * Read through the file looking for a restart page.  Since the restart
5228c2ecf20Sopenharmony_ci	 * page header is at the beginning of a page we only need to search at
5238c2ecf20Sopenharmony_ci	 * what could be the beginning of a page (for each page size) rather
5248c2ecf20Sopenharmony_ci	 * than scanning the whole file byte by byte.  If all potential places
5258c2ecf20Sopenharmony_ci	 * contain empty and uninitialzed records, the log file can be assumed
5268c2ecf20Sopenharmony_ci	 * to be empty.
5278c2ecf20Sopenharmony_ci	 */
5288c2ecf20Sopenharmony_ci	for (pos = 0; pos < size; pos <<= 1) {
5298c2ecf20Sopenharmony_ci		pgoff_t idx = pos >> PAGE_SHIFT;
5308c2ecf20Sopenharmony_ci		if (!page || page->index != idx) {
5318c2ecf20Sopenharmony_ci			if (page)
5328c2ecf20Sopenharmony_ci				ntfs_unmap_page(page);
5338c2ecf20Sopenharmony_ci			page = ntfs_map_page(mapping, idx);
5348c2ecf20Sopenharmony_ci			if (IS_ERR(page)) {
5358c2ecf20Sopenharmony_ci				ntfs_error(vol->sb, "Error mapping $LogFile "
5368c2ecf20Sopenharmony_ci						"page (index %lu).", idx);
5378c2ecf20Sopenharmony_ci				goto err_out;
5388c2ecf20Sopenharmony_ci			}
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci		kaddr = (u8*)page_address(page) + (pos & ~PAGE_MASK);
5418c2ecf20Sopenharmony_ci		/*
5428c2ecf20Sopenharmony_ci		 * A non-empty block means the logfile is not empty while an
5438c2ecf20Sopenharmony_ci		 * empty block after a non-empty block has been encountered
5448c2ecf20Sopenharmony_ci		 * means we are done.
5458c2ecf20Sopenharmony_ci		 */
5468c2ecf20Sopenharmony_ci		if (!ntfs_is_empty_recordp((le32*)kaddr))
5478c2ecf20Sopenharmony_ci			logfile_is_empty = false;
5488c2ecf20Sopenharmony_ci		else if (!logfile_is_empty)
5498c2ecf20Sopenharmony_ci			break;
5508c2ecf20Sopenharmony_ci		/*
5518c2ecf20Sopenharmony_ci		 * A log record page means there cannot be a restart page after
5528c2ecf20Sopenharmony_ci		 * this so no need to continue searching.
5538c2ecf20Sopenharmony_ci		 */
5548c2ecf20Sopenharmony_ci		if (ntfs_is_rcrd_recordp((le32*)kaddr))
5558c2ecf20Sopenharmony_ci			break;
5568c2ecf20Sopenharmony_ci		/* If not a (modified by chkdsk) restart page, continue. */
5578c2ecf20Sopenharmony_ci		if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
5588c2ecf20Sopenharmony_ci				!ntfs_is_chkd_recordp((le32*)kaddr)) {
5598c2ecf20Sopenharmony_ci			if (!pos)
5608c2ecf20Sopenharmony_ci				pos = NTFS_BLOCK_SIZE >> 1;
5618c2ecf20Sopenharmony_ci			continue;
5628c2ecf20Sopenharmony_ci		}
5638c2ecf20Sopenharmony_ci		/*
5648c2ecf20Sopenharmony_ci		 * Check the (modified by chkdsk) restart page for consistency
5658c2ecf20Sopenharmony_ci		 * and get a copy of the complete multi sector transfer
5668c2ecf20Sopenharmony_ci		 * deprotected restart page.
5678c2ecf20Sopenharmony_ci		 */
5688c2ecf20Sopenharmony_ci		err = ntfs_check_and_load_restart_page(log_vi,
5698c2ecf20Sopenharmony_ci				(RESTART_PAGE_HEADER*)kaddr, pos,
5708c2ecf20Sopenharmony_ci				!rstr1_ph ? &rstr1_ph : &rstr2_ph,
5718c2ecf20Sopenharmony_ci				!rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
5728c2ecf20Sopenharmony_ci		if (!err) {
5738c2ecf20Sopenharmony_ci			/*
5748c2ecf20Sopenharmony_ci			 * If we have now found the first (modified by chkdsk)
5758c2ecf20Sopenharmony_ci			 * restart page, continue looking for the second one.
5768c2ecf20Sopenharmony_ci			 */
5778c2ecf20Sopenharmony_ci			if (!pos) {
5788c2ecf20Sopenharmony_ci				pos = NTFS_BLOCK_SIZE >> 1;
5798c2ecf20Sopenharmony_ci				continue;
5808c2ecf20Sopenharmony_ci			}
5818c2ecf20Sopenharmony_ci			/*
5828c2ecf20Sopenharmony_ci			 * We have now found the second (modified by chkdsk)
5838c2ecf20Sopenharmony_ci			 * restart page, so we can stop looking.
5848c2ecf20Sopenharmony_ci			 */
5858c2ecf20Sopenharmony_ci			break;
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci		/*
5888c2ecf20Sopenharmony_ci		 * Error output already done inside the function.  Note, we do
5898c2ecf20Sopenharmony_ci		 * not abort if the restart page was invalid as we might still
5908c2ecf20Sopenharmony_ci		 * find a valid one further in the file.
5918c2ecf20Sopenharmony_ci		 */
5928c2ecf20Sopenharmony_ci		if (err != -EINVAL) {
5938c2ecf20Sopenharmony_ci			ntfs_unmap_page(page);
5948c2ecf20Sopenharmony_ci			goto err_out;
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci		/* Continue looking. */
5978c2ecf20Sopenharmony_ci		if (!pos)
5988c2ecf20Sopenharmony_ci			pos = NTFS_BLOCK_SIZE >> 1;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci	if (page)
6018c2ecf20Sopenharmony_ci		ntfs_unmap_page(page);
6028c2ecf20Sopenharmony_ci	if (logfile_is_empty) {
6038c2ecf20Sopenharmony_ci		NVolSetLogFileEmpty(vol);
6048c2ecf20Sopenharmony_ciis_empty:
6058c2ecf20Sopenharmony_ci		ntfs_debug("Done.  ($LogFile is empty.)");
6068c2ecf20Sopenharmony_ci		return true;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci	if (!rstr1_ph) {
6098c2ecf20Sopenharmony_ci		BUG_ON(rstr2_ph);
6108c2ecf20Sopenharmony_ci		ntfs_error(vol->sb, "Did not find any restart pages in "
6118c2ecf20Sopenharmony_ci				"$LogFile and it was not empty.");
6128c2ecf20Sopenharmony_ci		return false;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci	/* If both restart pages were found, use the more recent one. */
6158c2ecf20Sopenharmony_ci	if (rstr2_ph) {
6168c2ecf20Sopenharmony_ci		/*
6178c2ecf20Sopenharmony_ci		 * If the second restart area is more recent, switch to it.
6188c2ecf20Sopenharmony_ci		 * Otherwise just throw it away.
6198c2ecf20Sopenharmony_ci		 */
6208c2ecf20Sopenharmony_ci		if (rstr2_lsn > rstr1_lsn) {
6218c2ecf20Sopenharmony_ci			ntfs_debug("Using second restart page as it is more "
6228c2ecf20Sopenharmony_ci					"recent.");
6238c2ecf20Sopenharmony_ci			ntfs_free(rstr1_ph);
6248c2ecf20Sopenharmony_ci			rstr1_ph = rstr2_ph;
6258c2ecf20Sopenharmony_ci			/* rstr1_lsn = rstr2_lsn; */
6268c2ecf20Sopenharmony_ci		} else {
6278c2ecf20Sopenharmony_ci			ntfs_debug("Using first restart page as it is more "
6288c2ecf20Sopenharmony_ci					"recent.");
6298c2ecf20Sopenharmony_ci			ntfs_free(rstr2_ph);
6308c2ecf20Sopenharmony_ci		}
6318c2ecf20Sopenharmony_ci		rstr2_ph = NULL;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci	/* All consistency checks passed. */
6348c2ecf20Sopenharmony_ci	if (rp)
6358c2ecf20Sopenharmony_ci		*rp = rstr1_ph;
6368c2ecf20Sopenharmony_ci	else
6378c2ecf20Sopenharmony_ci		ntfs_free(rstr1_ph);
6388c2ecf20Sopenharmony_ci	ntfs_debug("Done.");
6398c2ecf20Sopenharmony_ci	return true;
6408c2ecf20Sopenharmony_cierr_out:
6418c2ecf20Sopenharmony_ci	if (rstr1_ph)
6428c2ecf20Sopenharmony_ci		ntfs_free(rstr1_ph);
6438c2ecf20Sopenharmony_ci	return false;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci/**
6478c2ecf20Sopenharmony_ci * ntfs_is_logfile_clean - check in the journal if the volume is clean
6488c2ecf20Sopenharmony_ci * @log_vi:	struct inode of loaded journal $LogFile to check
6498c2ecf20Sopenharmony_ci * @rp:		copy of the current restart page
6508c2ecf20Sopenharmony_ci *
6518c2ecf20Sopenharmony_ci * Analyze the $LogFile journal and return 'true' if it indicates the volume was
6528c2ecf20Sopenharmony_ci * shutdown cleanly and 'false' if not.
6538c2ecf20Sopenharmony_ci *
6548c2ecf20Sopenharmony_ci * At present we only look at the two restart pages and ignore the log record
6558c2ecf20Sopenharmony_ci * pages.  This is a little bit crude in that there will be a very small number
6568c2ecf20Sopenharmony_ci * of cases where we think that a volume is dirty when in fact it is clean.
6578c2ecf20Sopenharmony_ci * This should only affect volumes that have not been shutdown cleanly but did
6588c2ecf20Sopenharmony_ci * not have any pending, non-check-pointed i/o, i.e. they were completely idle
6598c2ecf20Sopenharmony_ci * at least for the five seconds preceding the unclean shutdown.
6608c2ecf20Sopenharmony_ci *
6618c2ecf20Sopenharmony_ci * This function assumes that the $LogFile journal has already been consistency
6628c2ecf20Sopenharmony_ci * checked by a call to ntfs_check_logfile() and in particular if the $LogFile
6638c2ecf20Sopenharmony_ci * is empty this function requires that NVolLogFileEmpty() is true otherwise an
6648c2ecf20Sopenharmony_ci * empty volume will be reported as dirty.
6658c2ecf20Sopenharmony_ci */
6668c2ecf20Sopenharmony_cibool ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER *rp)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
6698c2ecf20Sopenharmony_ci	RESTART_AREA *ra;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
6728c2ecf20Sopenharmony_ci	/* An empty $LogFile must have been clean before it got emptied. */
6738c2ecf20Sopenharmony_ci	if (NVolLogFileEmpty(vol)) {
6748c2ecf20Sopenharmony_ci		ntfs_debug("Done.  ($LogFile is empty.)");
6758c2ecf20Sopenharmony_ci		return true;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci	BUG_ON(!rp);
6788c2ecf20Sopenharmony_ci	if (!ntfs_is_rstr_record(rp->magic) &&
6798c2ecf20Sopenharmony_ci			!ntfs_is_chkd_record(rp->magic)) {
6808c2ecf20Sopenharmony_ci		ntfs_error(vol->sb, "Restart page buffer is invalid.  This is "
6818c2ecf20Sopenharmony_ci				"probably a bug in that the $LogFile should "
6828c2ecf20Sopenharmony_ci				"have been consistency checked before calling "
6838c2ecf20Sopenharmony_ci				"this function.");
6848c2ecf20Sopenharmony_ci		return false;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci	ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
6878c2ecf20Sopenharmony_ci	/*
6888c2ecf20Sopenharmony_ci	 * If the $LogFile has active clients, i.e. it is open, and we do not
6898c2ecf20Sopenharmony_ci	 * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
6908c2ecf20Sopenharmony_ci	 * we assume there was an unclean shutdown.
6918c2ecf20Sopenharmony_ci	 */
6928c2ecf20Sopenharmony_ci	if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
6938c2ecf20Sopenharmony_ci			!(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
6948c2ecf20Sopenharmony_ci		ntfs_debug("Done.  $LogFile indicates a dirty shutdown.");
6958c2ecf20Sopenharmony_ci		return false;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci	/* $LogFile indicates a clean shutdown. */
6988c2ecf20Sopenharmony_ci	ntfs_debug("Done.  $LogFile indicates a clean shutdown.");
6998c2ecf20Sopenharmony_ci	return true;
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci/**
7038c2ecf20Sopenharmony_ci * ntfs_empty_logfile - empty the contents of the $LogFile journal
7048c2ecf20Sopenharmony_ci * @log_vi:	struct inode of loaded journal $LogFile to empty
7058c2ecf20Sopenharmony_ci *
7068c2ecf20Sopenharmony_ci * Empty the contents of the $LogFile journal @log_vi and return 'true' on
7078c2ecf20Sopenharmony_ci * success and 'false' on error.
7088c2ecf20Sopenharmony_ci *
7098c2ecf20Sopenharmony_ci * This function assumes that the $LogFile journal has already been consistency
7108c2ecf20Sopenharmony_ci * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
7118c2ecf20Sopenharmony_ci * has been used to ensure that the $LogFile is clean.
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_cibool ntfs_empty_logfile(struct inode *log_vi)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	VCN vcn, end_vcn;
7168c2ecf20Sopenharmony_ci	ntfs_inode *log_ni = NTFS_I(log_vi);
7178c2ecf20Sopenharmony_ci	ntfs_volume *vol = log_ni->vol;
7188c2ecf20Sopenharmony_ci	struct super_block *sb = vol->sb;
7198c2ecf20Sopenharmony_ci	runlist_element *rl;
7208c2ecf20Sopenharmony_ci	unsigned long flags;
7218c2ecf20Sopenharmony_ci	unsigned block_size, block_size_bits;
7228c2ecf20Sopenharmony_ci	int err;
7238c2ecf20Sopenharmony_ci	bool should_wait = true;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	ntfs_debug("Entering.");
7268c2ecf20Sopenharmony_ci	if (NVolLogFileEmpty(vol)) {
7278c2ecf20Sopenharmony_ci		ntfs_debug("Done.");
7288c2ecf20Sopenharmony_ci		return true;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci	/*
7318c2ecf20Sopenharmony_ci	 * We cannot use ntfs_attr_set() because we may be still in the middle
7328c2ecf20Sopenharmony_ci	 * of a mount operation.  Thus we do the emptying by hand by first
7338c2ecf20Sopenharmony_ci	 * zapping the page cache pages for the $LogFile/$DATA attribute and
7348c2ecf20Sopenharmony_ci	 * then emptying each of the buffers in each of the clusters specified
7358c2ecf20Sopenharmony_ci	 * by the runlist by hand.
7368c2ecf20Sopenharmony_ci	 */
7378c2ecf20Sopenharmony_ci	block_size = sb->s_blocksize;
7388c2ecf20Sopenharmony_ci	block_size_bits = sb->s_blocksize_bits;
7398c2ecf20Sopenharmony_ci	vcn = 0;
7408c2ecf20Sopenharmony_ci	read_lock_irqsave(&log_ni->size_lock, flags);
7418c2ecf20Sopenharmony_ci	end_vcn = (log_ni->initialized_size + vol->cluster_size_mask) >>
7428c2ecf20Sopenharmony_ci			vol->cluster_size_bits;
7438c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&log_ni->size_lock, flags);
7448c2ecf20Sopenharmony_ci	truncate_inode_pages(log_vi->i_mapping, 0);
7458c2ecf20Sopenharmony_ci	down_write(&log_ni->runlist.lock);
7468c2ecf20Sopenharmony_ci	rl = log_ni->runlist.rl;
7478c2ecf20Sopenharmony_ci	if (unlikely(!rl || vcn < rl->vcn || !rl->length)) {
7488c2ecf20Sopenharmony_cimap_vcn:
7498c2ecf20Sopenharmony_ci		err = ntfs_map_runlist_nolock(log_ni, vcn, NULL);
7508c2ecf20Sopenharmony_ci		if (err) {
7518c2ecf20Sopenharmony_ci			ntfs_error(sb, "Failed to map runlist fragment (error "
7528c2ecf20Sopenharmony_ci					"%d).", -err);
7538c2ecf20Sopenharmony_ci			goto err;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci		rl = log_ni->runlist.rl;
7568c2ecf20Sopenharmony_ci		BUG_ON(!rl || vcn < rl->vcn || !rl->length);
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci	/* Seek to the runlist element containing @vcn. */
7598c2ecf20Sopenharmony_ci	while (rl->length && vcn >= rl[1].vcn)
7608c2ecf20Sopenharmony_ci		rl++;
7618c2ecf20Sopenharmony_ci	do {
7628c2ecf20Sopenharmony_ci		LCN lcn;
7638c2ecf20Sopenharmony_ci		sector_t block, end_block;
7648c2ecf20Sopenharmony_ci		s64 len;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		/*
7678c2ecf20Sopenharmony_ci		 * If this run is not mapped map it now and start again as the
7688c2ecf20Sopenharmony_ci		 * runlist will have been updated.
7698c2ecf20Sopenharmony_ci		 */
7708c2ecf20Sopenharmony_ci		lcn = rl->lcn;
7718c2ecf20Sopenharmony_ci		if (unlikely(lcn == LCN_RL_NOT_MAPPED)) {
7728c2ecf20Sopenharmony_ci			vcn = rl->vcn;
7738c2ecf20Sopenharmony_ci			goto map_vcn;
7748c2ecf20Sopenharmony_ci		}
7758c2ecf20Sopenharmony_ci		/* If this run is not valid abort with an error. */
7768c2ecf20Sopenharmony_ci		if (unlikely(!rl->length || lcn < LCN_HOLE))
7778c2ecf20Sopenharmony_ci			goto rl_err;
7788c2ecf20Sopenharmony_ci		/* Skip holes. */
7798c2ecf20Sopenharmony_ci		if (lcn == LCN_HOLE)
7808c2ecf20Sopenharmony_ci			continue;
7818c2ecf20Sopenharmony_ci		block = lcn << vol->cluster_size_bits >> block_size_bits;
7828c2ecf20Sopenharmony_ci		len = rl->length;
7838c2ecf20Sopenharmony_ci		if (rl[1].vcn > end_vcn)
7848c2ecf20Sopenharmony_ci			len = end_vcn - rl->vcn;
7858c2ecf20Sopenharmony_ci		end_block = (lcn + len) << vol->cluster_size_bits >>
7868c2ecf20Sopenharmony_ci				block_size_bits;
7878c2ecf20Sopenharmony_ci		/* Iterate over the blocks in the run and empty them. */
7888c2ecf20Sopenharmony_ci		do {
7898c2ecf20Sopenharmony_ci			struct buffer_head *bh;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci			/* Obtain the buffer, possibly not uptodate. */
7928c2ecf20Sopenharmony_ci			bh = sb_getblk(sb, block);
7938c2ecf20Sopenharmony_ci			BUG_ON(!bh);
7948c2ecf20Sopenharmony_ci			/* Setup buffer i/o submission. */
7958c2ecf20Sopenharmony_ci			lock_buffer(bh);
7968c2ecf20Sopenharmony_ci			bh->b_end_io = end_buffer_write_sync;
7978c2ecf20Sopenharmony_ci			get_bh(bh);
7988c2ecf20Sopenharmony_ci			/* Set the entire contents of the buffer to 0xff. */
7998c2ecf20Sopenharmony_ci			memset(bh->b_data, -1, block_size);
8008c2ecf20Sopenharmony_ci			if (!buffer_uptodate(bh))
8018c2ecf20Sopenharmony_ci				set_buffer_uptodate(bh);
8028c2ecf20Sopenharmony_ci			if (buffer_dirty(bh))
8038c2ecf20Sopenharmony_ci				clear_buffer_dirty(bh);
8048c2ecf20Sopenharmony_ci			/*
8058c2ecf20Sopenharmony_ci			 * Submit the buffer and wait for i/o to complete but
8068c2ecf20Sopenharmony_ci			 * only for the first buffer so we do not miss really
8078c2ecf20Sopenharmony_ci			 * serious i/o errors.  Once the first buffer has
8088c2ecf20Sopenharmony_ci			 * completed ignore errors afterwards as we can assume
8098c2ecf20Sopenharmony_ci			 * that if one buffer worked all of them will work.
8108c2ecf20Sopenharmony_ci			 */
8118c2ecf20Sopenharmony_ci			submit_bh(REQ_OP_WRITE, 0, bh);
8128c2ecf20Sopenharmony_ci			if (should_wait) {
8138c2ecf20Sopenharmony_ci				should_wait = false;
8148c2ecf20Sopenharmony_ci				wait_on_buffer(bh);
8158c2ecf20Sopenharmony_ci				if (unlikely(!buffer_uptodate(bh)))
8168c2ecf20Sopenharmony_ci					goto io_err;
8178c2ecf20Sopenharmony_ci			}
8188c2ecf20Sopenharmony_ci			brelse(bh);
8198c2ecf20Sopenharmony_ci		} while (++block < end_block);
8208c2ecf20Sopenharmony_ci	} while ((++rl)->vcn < end_vcn);
8218c2ecf20Sopenharmony_ci	up_write(&log_ni->runlist.lock);
8228c2ecf20Sopenharmony_ci	/*
8238c2ecf20Sopenharmony_ci	 * Zap the pages again just in case any got instantiated whilst we were
8248c2ecf20Sopenharmony_ci	 * emptying the blocks by hand.  FIXME: We may not have completed
8258c2ecf20Sopenharmony_ci	 * writing to all the buffer heads yet so this may happen too early.
8268c2ecf20Sopenharmony_ci	 * We really should use a kernel thread to do the emptying
8278c2ecf20Sopenharmony_ci	 * asynchronously and then we can also set the volume dirty and output
8288c2ecf20Sopenharmony_ci	 * an error message if emptying should fail.
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	truncate_inode_pages(log_vi->i_mapping, 0);
8318c2ecf20Sopenharmony_ci	/* Set the flag so we do not have to do it again on remount. */
8328c2ecf20Sopenharmony_ci	NVolSetLogFileEmpty(vol);
8338c2ecf20Sopenharmony_ci	ntfs_debug("Done.");
8348c2ecf20Sopenharmony_ci	return true;
8358c2ecf20Sopenharmony_ciio_err:
8368c2ecf20Sopenharmony_ci	ntfs_error(sb, "Failed to write buffer.  Unmount and run chkdsk.");
8378c2ecf20Sopenharmony_ci	goto dirty_err;
8388c2ecf20Sopenharmony_cirl_err:
8398c2ecf20Sopenharmony_ci	ntfs_error(sb, "Runlist is corrupt.  Unmount and run chkdsk.");
8408c2ecf20Sopenharmony_cidirty_err:
8418c2ecf20Sopenharmony_ci	NVolSetErrors(vol);
8428c2ecf20Sopenharmony_ci	err = -EIO;
8438c2ecf20Sopenharmony_cierr:
8448c2ecf20Sopenharmony_ci	up_write(&log_ni->runlist.lock);
8458c2ecf20Sopenharmony_ci	ntfs_error(sb, "Failed to fill $LogFile with 0xff bytes (error %d).",
8468c2ecf20Sopenharmony_ci			-err);
8478c2ecf20Sopenharmony_ci	return false;
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci#endif /* NTFS_RW */
851