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