180bca3d3SAxel Dörfler /** 280bca3d3SAxel Dörfler * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project. 380bca3d3SAxel Dörfler * 480bca3d3SAxel Dörfler * Copyright (c) 2002-2005 Anton Altaparmakov 580bca3d3SAxel Dörfler * Copyright (c) 2005 Yura Pakhuchiy 680bca3d3SAxel Dörfler * Copyright (c) 2005-2006 Szabolcs Szakacsits 780bca3d3SAxel Dörfler * 880bca3d3SAxel Dörfler * This program/include file is free software; you can redistribute it and/or 980bca3d3SAxel Dörfler * modify it under the terms of the GNU General Public License as published 1080bca3d3SAxel Dörfler * by the Free Software Foundation; either version 2 of the License, or 1180bca3d3SAxel Dörfler * (at your option) any later version. 1280bca3d3SAxel Dörfler * 1380bca3d3SAxel Dörfler * This program/include file is distributed in the hope that it will be 1480bca3d3SAxel Dörfler * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 1580bca3d3SAxel Dörfler * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1680bca3d3SAxel Dörfler * GNU General Public License for more details. 1780bca3d3SAxel Dörfler * 1880bca3d3SAxel Dörfler * You should have received a copy of the GNU General Public License 1980bca3d3SAxel Dörfler * along with this program (in the main directory of the NTFS-3G 2080bca3d3SAxel Dörfler * distribution in the file COPYING); if not, write to the Free Software 2180bca3d3SAxel Dörfler * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2280bca3d3SAxel Dörfler */ 2380bca3d3SAxel Dörfler 2480bca3d3SAxel Dörfler #ifdef HAVE_CONFIG_H 2580bca3d3SAxel Dörfler #include "config.h" 2680bca3d3SAxel Dörfler #endif 2780bca3d3SAxel Dörfler 2880bca3d3SAxel Dörfler #ifdef HAVE_STDLIB_H 2980bca3d3SAxel Dörfler #include <stdlib.h> 3080bca3d3SAxel Dörfler #endif 3180bca3d3SAxel Dörfler #ifdef HAVE_STRING_H 3280bca3d3SAxel Dörfler #include <string.h> 3380bca3d3SAxel Dörfler #endif 3480bca3d3SAxel Dörfler #ifdef HAVE_ERRNO_H 3580bca3d3SAxel Dörfler #include <errno.h> 3680bca3d3SAxel Dörfler #endif 3780bca3d3SAxel Dörfler 3880bca3d3SAxel Dörfler #include "attrib.h" 3980bca3d3SAxel Dörfler #include "debug.h" 4080bca3d3SAxel Dörfler #include "logfile.h" 4180bca3d3SAxel Dörfler #include "volume.h" 4280bca3d3SAxel Dörfler #include "mst.h" 4380bca3d3SAxel Dörfler #include "logging.h" 4480bca3d3SAxel Dörfler #include "misc.h" 4580bca3d3SAxel Dörfler 4680bca3d3SAxel Dörfler /** 4780bca3d3SAxel Dörfler * ntfs_check_restart_page_header - check the page header for consistency 4880bca3d3SAxel Dörfler * @rp: restart page header to check 4980bca3d3SAxel Dörfler * @pos: position in logfile at which the restart page header resides 5080bca3d3SAxel Dörfler * 5180bca3d3SAxel Dörfler * Check the restart page header @rp for consistency and return TRUE if it is 5280bca3d3SAxel Dörfler * consistent and FALSE otherwise. 5380bca3d3SAxel Dörfler * 5480bca3d3SAxel Dörfler * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 5580bca3d3SAxel Dörfler * require the full restart page. 5680bca3d3SAxel Dörfler */ 5780bca3d3SAxel Dörfler static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) 5880bca3d3SAxel Dörfler { 5980bca3d3SAxel Dörfler u32 logfile_system_page_size, logfile_log_page_size; 6080bca3d3SAxel Dörfler u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; 6180bca3d3SAxel Dörfler BOOL have_usa = TRUE; 6280bca3d3SAxel Dörfler 6380bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 6480bca3d3SAxel Dörfler /* 6580bca3d3SAxel Dörfler * If the system or log page sizes are smaller than the ntfs block size 6680bca3d3SAxel Dörfler * or either is not a power of 2 we cannot handle this log file. 6780bca3d3SAxel Dörfler */ 6880bca3d3SAxel Dörfler logfile_system_page_size = le32_to_cpu(rp->system_page_size); 6980bca3d3SAxel Dörfler logfile_log_page_size = le32_to_cpu(rp->log_page_size); 7080bca3d3SAxel Dörfler if (logfile_system_page_size < NTFS_BLOCK_SIZE || 7180bca3d3SAxel Dörfler logfile_log_page_size < NTFS_BLOCK_SIZE || 7280bca3d3SAxel Dörfler logfile_system_page_size & 7380bca3d3SAxel Dörfler (logfile_system_page_size - 1) || 7480bca3d3SAxel Dörfler logfile_log_page_size & (logfile_log_page_size - 1)) { 7580bca3d3SAxel Dörfler ntfs_log_error("$LogFile uses unsupported page size.\n"); 7680bca3d3SAxel Dörfler return FALSE; 7780bca3d3SAxel Dörfler } 7880bca3d3SAxel Dörfler /* 7980bca3d3SAxel Dörfler * We must be either at !pos (1st restart page) or at pos = system page 8080bca3d3SAxel Dörfler * size (2nd restart page). 8180bca3d3SAxel Dörfler */ 8280bca3d3SAxel Dörfler if (pos && pos != logfile_system_page_size) { 8380bca3d3SAxel Dörfler ntfs_log_error("Found restart area in incorrect " 8480bca3d3SAxel Dörfler "position in $LogFile.\n"); 8580bca3d3SAxel Dörfler return FALSE; 8680bca3d3SAxel Dörfler } 8780bca3d3SAxel Dörfler /* We only know how to handle version 1.1. */ 8880bca3d3SAxel Dörfler if (sle16_to_cpu(rp->major_ver) != 1 || 8980bca3d3SAxel Dörfler sle16_to_cpu(rp->minor_ver) != 1) { 9080bca3d3SAxel Dörfler ntfs_log_error("$LogFile version %i.%i is not " 9180bca3d3SAxel Dörfler "supported. (This driver supports version " 9280bca3d3SAxel Dörfler "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver), 9380bca3d3SAxel Dörfler (int)sle16_to_cpu(rp->minor_ver)); 9480bca3d3SAxel Dörfler return FALSE; 9580bca3d3SAxel Dörfler } 9680bca3d3SAxel Dörfler /* 9780bca3d3SAxel Dörfler * If chkdsk has been run the restart page may not be protected by an 9880bca3d3SAxel Dörfler * update sequence array. 9980bca3d3SAxel Dörfler */ 10080bca3d3SAxel Dörfler if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { 10180bca3d3SAxel Dörfler have_usa = FALSE; 10280bca3d3SAxel Dörfler goto skip_usa_checks; 10380bca3d3SAxel Dörfler } 10480bca3d3SAxel Dörfler /* Verify the size of the update sequence array. */ 10580bca3d3SAxel Dörfler usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); 10680bca3d3SAxel Dörfler if (usa_count != le16_to_cpu(rp->usa_count)) { 10780bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page specifies " 10880bca3d3SAxel Dörfler "inconsistent update sequence array count.\n"); 10980bca3d3SAxel Dörfler return FALSE; 11080bca3d3SAxel Dörfler } 11180bca3d3SAxel Dörfler /* Verify the position of the update sequence array. */ 11280bca3d3SAxel Dörfler usa_ofs = le16_to_cpu(rp->usa_ofs); 11380bca3d3SAxel Dörfler usa_end = usa_ofs + usa_count * sizeof(u16); 11480bca3d3SAxel Dörfler if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || 11580bca3d3SAxel Dörfler usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { 11680bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page specifies " 11780bca3d3SAxel Dörfler "inconsistent update sequence array offset.\n"); 11880bca3d3SAxel Dörfler return FALSE; 11980bca3d3SAxel Dörfler } 12080bca3d3SAxel Dörfler skip_usa_checks: 12180bca3d3SAxel Dörfler /* 12280bca3d3SAxel Dörfler * Verify the position of the restart area. It must be: 12380bca3d3SAxel Dörfler * - aligned to 8-byte boundary, 12480bca3d3SAxel Dörfler * - after the update sequence array, and 12580bca3d3SAxel Dörfler * - within the system page size. 12680bca3d3SAxel Dörfler */ 12780bca3d3SAxel Dörfler ra_ofs = le16_to_cpu(rp->restart_area_offset); 12880bca3d3SAxel Dörfler if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : 12980bca3d3SAxel Dörfler ra_ofs < sizeof(RESTART_PAGE_HEADER)) || 13080bca3d3SAxel Dörfler ra_ofs > logfile_system_page_size) { 13180bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page specifies " 13280bca3d3SAxel Dörfler "inconsistent restart area offset.\n"); 13380bca3d3SAxel Dörfler return FALSE; 13480bca3d3SAxel Dörfler } 13580bca3d3SAxel Dörfler /* 13680bca3d3SAxel Dörfler * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn 13780bca3d3SAxel Dörfler * set. 13880bca3d3SAxel Dörfler */ 13980bca3d3SAxel Dörfler if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { 14080bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page is not modified " 14180bca3d3SAxel Dörfler "by chkdsk but a chkdsk LSN is specified.\n"); 14280bca3d3SAxel Dörfler return FALSE; 14380bca3d3SAxel Dörfler } 14480bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 14580bca3d3SAxel Dörfler return TRUE; 14680bca3d3SAxel Dörfler } 14780bca3d3SAxel Dörfler 14880bca3d3SAxel Dörfler /** 14980bca3d3SAxel Dörfler * ntfs_check_restart_area - check the restart area for consistency 15080bca3d3SAxel Dörfler * @rp: restart page whose restart area to check 15180bca3d3SAxel Dörfler * 15280bca3d3SAxel Dörfler * Check the restart area of the restart page @rp for consistency and return 15380bca3d3SAxel Dörfler * TRUE if it is consistent and FALSE otherwise. 15480bca3d3SAxel Dörfler * 15580bca3d3SAxel Dörfler * This function assumes that the restart page header has already been 15680bca3d3SAxel Dörfler * consistency checked. 15780bca3d3SAxel Dörfler * 15880bca3d3SAxel Dörfler * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 15980bca3d3SAxel Dörfler * require the full restart page. 16080bca3d3SAxel Dörfler */ 16180bca3d3SAxel Dörfler static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) 16280bca3d3SAxel Dörfler { 16380bca3d3SAxel Dörfler u64 file_size; 16480bca3d3SAxel Dörfler RESTART_AREA *ra; 16580bca3d3SAxel Dörfler u16 ra_ofs, ra_len, ca_ofs; 16680bca3d3SAxel Dörfler u8 fs_bits; 16780bca3d3SAxel Dörfler 16880bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 16980bca3d3SAxel Dörfler ra_ofs = le16_to_cpu(rp->restart_area_offset); 17080bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + ra_ofs); 17180bca3d3SAxel Dörfler /* 17280bca3d3SAxel Dörfler * Everything before ra->file_size must be before the first word 17380bca3d3SAxel Dörfler * protected by an update sequence number. This ensures that it is 17480bca3d3SAxel Dörfler * safe to access ra->client_array_offset. 17580bca3d3SAxel Dörfler */ 17680bca3d3SAxel Dörfler if (ra_ofs + offsetof(RESTART_AREA, file_size) > 17780bca3d3SAxel Dörfler NTFS_BLOCK_SIZE - sizeof(u16)) { 17880bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 17980bca3d3SAxel Dörfler "inconsistent file offset.\n"); 18080bca3d3SAxel Dörfler return FALSE; 18180bca3d3SAxel Dörfler } 18280bca3d3SAxel Dörfler /* 18380bca3d3SAxel Dörfler * Now that we can access ra->client_array_offset, make sure everything 18480bca3d3SAxel Dörfler * up to the log client array is before the first word protected by an 18580bca3d3SAxel Dörfler * update sequence number. This ensures we can access all of the 18680bca3d3SAxel Dörfler * restart area elements safely. Also, the client array offset must be 18780bca3d3SAxel Dörfler * aligned to an 8-byte boundary. 18880bca3d3SAxel Dörfler */ 18980bca3d3SAxel Dörfler ca_ofs = le16_to_cpu(ra->client_array_offset); 19080bca3d3SAxel Dörfler if (((ca_ofs + 7) & ~7) != ca_ofs || 19180bca3d3SAxel Dörfler ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - 19280bca3d3SAxel Dörfler sizeof(u16))) { 19380bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 19480bca3d3SAxel Dörfler "inconsistent client array offset.\n"); 19580bca3d3SAxel Dörfler return FALSE; 19680bca3d3SAxel Dörfler } 19780bca3d3SAxel Dörfler /* 19880bca3d3SAxel Dörfler * The restart area must end within the system page size both when 19980bca3d3SAxel Dörfler * calculated manually and as specified by ra->restart_area_length. 20080bca3d3SAxel Dörfler * Also, the calculated length must not exceed the specified length. 20180bca3d3SAxel Dörfler */ 20280bca3d3SAxel Dörfler ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * 20380bca3d3SAxel Dörfler sizeof(LOG_CLIENT_RECORD); 20480bca3d3SAxel Dörfler if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || 20580bca3d3SAxel Dörfler (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > 20680bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size) || 20780bca3d3SAxel Dörfler ra_len > le16_to_cpu(ra->restart_area_length)) { 20880bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area is out of bounds " 20980bca3d3SAxel Dörfler "of the system page size specified by the " 21080bca3d3SAxel Dörfler "restart page header and/or the specified " 21180bca3d3SAxel Dörfler "restart area length is inconsistent.\n"); 21280bca3d3SAxel Dörfler return FALSE; 21380bca3d3SAxel Dörfler } 21480bca3d3SAxel Dörfler /* 21580bca3d3SAxel Dörfler * The ra->client_free_list and ra->client_in_use_list must be either 21680bca3d3SAxel Dörfler * LOGFILE_NO_CLIENT or less than ra->log_clients or they are 21780bca3d3SAxel Dörfler * overflowing the client array. 21880bca3d3SAxel Dörfler */ 21980bca3d3SAxel Dörfler if ((ra->client_free_list != LOGFILE_NO_CLIENT && 22080bca3d3SAxel Dörfler le16_to_cpu(ra->client_free_list) >= 22180bca3d3SAxel Dörfler le16_to_cpu(ra->log_clients)) || 22280bca3d3SAxel Dörfler (ra->client_in_use_list != LOGFILE_NO_CLIENT && 22380bca3d3SAxel Dörfler le16_to_cpu(ra->client_in_use_list) >= 22480bca3d3SAxel Dörfler le16_to_cpu(ra->log_clients))) { 22580bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 22680bca3d3SAxel Dörfler "overflowing client free and/or in use lists.\n"); 22780bca3d3SAxel Dörfler return FALSE; 22880bca3d3SAxel Dörfler } 22980bca3d3SAxel Dörfler /* 23080bca3d3SAxel Dörfler * Check ra->seq_number_bits against ra->file_size for consistency. 23180bca3d3SAxel Dörfler * We cannot just use ffs() because the file size is not a power of 2. 23280bca3d3SAxel Dörfler */ 23380bca3d3SAxel Dörfler file_size = (u64)sle64_to_cpu(ra->file_size); 23480bca3d3SAxel Dörfler fs_bits = 0; 23580bca3d3SAxel Dörfler while (file_size) { 23680bca3d3SAxel Dörfler file_size >>= 1; 23780bca3d3SAxel Dörfler fs_bits++; 23880bca3d3SAxel Dörfler } 23980bca3d3SAxel Dörfler if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { 24080bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 24180bca3d3SAxel Dörfler "inconsistent sequence number bits.\n"); 24280bca3d3SAxel Dörfler return FALSE; 24380bca3d3SAxel Dörfler } 24480bca3d3SAxel Dörfler /* The log record header length must be a multiple of 8. */ 24580bca3d3SAxel Dörfler if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != 24680bca3d3SAxel Dörfler le16_to_cpu(ra->log_record_header_length)) { 24780bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 24880bca3d3SAxel Dörfler "inconsistent log record header length.\n"); 24980bca3d3SAxel Dörfler return FALSE; 25080bca3d3SAxel Dörfler } 25180bca3d3SAxel Dörfler /* Ditto for the log page data offset. */ 25280bca3d3SAxel Dörfler if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != 25380bca3d3SAxel Dörfler le16_to_cpu(ra->log_page_data_offset)) { 25480bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 25580bca3d3SAxel Dörfler "inconsistent log page data offset.\n"); 25680bca3d3SAxel Dörfler return FALSE; 25780bca3d3SAxel Dörfler } 25880bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 25980bca3d3SAxel Dörfler return TRUE; 26080bca3d3SAxel Dörfler } 26180bca3d3SAxel Dörfler 26280bca3d3SAxel Dörfler /** 26380bca3d3SAxel Dörfler * ntfs_check_log_client_array - check the log client array for consistency 26480bca3d3SAxel Dörfler * @rp: restart page whose log client array to check 26580bca3d3SAxel Dörfler * 26680bca3d3SAxel Dörfler * Check the log client array of the restart page @rp for consistency and 26780bca3d3SAxel Dörfler * return TRUE if it is consistent and FALSE otherwise. 26880bca3d3SAxel Dörfler * 26980bca3d3SAxel Dörfler * This function assumes that the restart page header and the restart area have 27080bca3d3SAxel Dörfler * already been consistency checked. 27180bca3d3SAxel Dörfler * 27280bca3d3SAxel Dörfler * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this 27380bca3d3SAxel Dörfler * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full 27480bca3d3SAxel Dörfler * restart page and the page must be multi sector transfer deprotected. 27580bca3d3SAxel Dörfler */ 27680bca3d3SAxel Dörfler static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) 27780bca3d3SAxel Dörfler { 27880bca3d3SAxel Dörfler RESTART_AREA *ra; 27980bca3d3SAxel Dörfler LOG_CLIENT_RECORD *ca, *cr; 28080bca3d3SAxel Dörfler u16 nr_clients, idx; 28180bca3d3SAxel Dörfler BOOL in_free_list, idx_is_first; 28280bca3d3SAxel Dörfler 28380bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 28480bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 28580bca3d3SAxel Dörfler ca = (LOG_CLIENT_RECORD*)((u8*)ra + 28680bca3d3SAxel Dörfler le16_to_cpu(ra->client_array_offset)); 28780bca3d3SAxel Dörfler /* 28880bca3d3SAxel Dörfler * Check the ra->client_free_list first and then check the 28980bca3d3SAxel Dörfler * ra->client_in_use_list. Check each of the log client records in 29080bca3d3SAxel Dörfler * each of the lists and check that the array does not overflow the 29180bca3d3SAxel Dörfler * ra->log_clients value. Also keep track of the number of records 29280bca3d3SAxel Dörfler * visited as there cannot be more than ra->log_clients records and 29380bca3d3SAxel Dörfler * that way we detect eventual loops in within a list. 29480bca3d3SAxel Dörfler */ 29580bca3d3SAxel Dörfler nr_clients = le16_to_cpu(ra->log_clients); 29680bca3d3SAxel Dörfler idx = le16_to_cpu(ra->client_free_list); 29780bca3d3SAxel Dörfler in_free_list = TRUE; 29880bca3d3SAxel Dörfler check_list: 29980bca3d3SAxel Dörfler for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, 30080bca3d3SAxel Dörfler idx = le16_to_cpu(cr->next_client)) { 30180bca3d3SAxel Dörfler if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) 30280bca3d3SAxel Dörfler goto err_out; 30380bca3d3SAxel Dörfler /* Set @cr to the current log client record. */ 30480bca3d3SAxel Dörfler cr = ca + idx; 30580bca3d3SAxel Dörfler /* The first log client record must not have a prev_client. */ 30680bca3d3SAxel Dörfler if (idx_is_first) { 30780bca3d3SAxel Dörfler if (cr->prev_client != LOGFILE_NO_CLIENT) 30880bca3d3SAxel Dörfler goto err_out; 30980bca3d3SAxel Dörfler idx_is_first = FALSE; 31080bca3d3SAxel Dörfler } 31180bca3d3SAxel Dörfler } 31280bca3d3SAxel Dörfler /* Switch to and check the in use list if we just did the free list. */ 31380bca3d3SAxel Dörfler if (in_free_list) { 31480bca3d3SAxel Dörfler in_free_list = FALSE; 31580bca3d3SAxel Dörfler idx = le16_to_cpu(ra->client_in_use_list); 31680bca3d3SAxel Dörfler goto check_list; 31780bca3d3SAxel Dörfler } 31880bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 31980bca3d3SAxel Dörfler return TRUE; 32080bca3d3SAxel Dörfler err_out: 32180bca3d3SAxel Dörfler ntfs_log_error("$LogFile log client array is corrupt.\n"); 32280bca3d3SAxel Dörfler return FALSE; 32380bca3d3SAxel Dörfler } 32480bca3d3SAxel Dörfler 32580bca3d3SAxel Dörfler /** 32680bca3d3SAxel Dörfler * ntfs_check_and_load_restart_page - check the restart page for consistency 32780bca3d3SAxel Dörfler * @log_na: opened ntfs attribute for journal $LogFile 32880bca3d3SAxel Dörfler * @rp: restart page to check 32980bca3d3SAxel Dörfler * @pos: position in @log_na at which the restart page resides 33080bca3d3SAxel Dörfler * @wrp: [OUT] copy of the multi sector transfer deprotected restart page 33180bca3d3SAxel Dörfler * @lsn: [OUT] set to the current logfile lsn on success 33280bca3d3SAxel Dörfler * 33380bca3d3SAxel Dörfler * Check the restart page @rp for consistency and return 0 if it is consistent 33480bca3d3SAxel Dörfler * and errno otherwise. The restart page may have been modified by chkdsk in 33580bca3d3SAxel Dörfler * which case its magic is CHKD instead of RSTR. 33680bca3d3SAxel Dörfler * 33780bca3d3SAxel Dörfler * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 33880bca3d3SAxel Dörfler * require the full restart page. 33980bca3d3SAxel Dörfler * 34080bca3d3SAxel Dörfler * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a 34180bca3d3SAxel Dörfler * copy of the complete multi sector transfer deprotected page. On failure, 34280bca3d3SAxel Dörfler * *@wrp is undefined. 34380bca3d3SAxel Dörfler * 34480bca3d3SAxel Dörfler * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current 34580bca3d3SAxel Dörfler * logfile lsn according to this restart page. On failure, *@lsn is undefined. 34680bca3d3SAxel Dörfler * 34780bca3d3SAxel Dörfler * The following error codes are defined: 34880bca3d3SAxel Dörfler * EINVAL - The restart page is inconsistent. 34980bca3d3SAxel Dörfler * ENOMEM - Not enough memory to load the restart page. 35080bca3d3SAxel Dörfler * EIO - Failed to reading from $LogFile. 35180bca3d3SAxel Dörfler */ 35280bca3d3SAxel Dörfler static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, 35380bca3d3SAxel Dörfler RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, 35480bca3d3SAxel Dörfler LSN *lsn) 35580bca3d3SAxel Dörfler { 35680bca3d3SAxel Dörfler RESTART_AREA *ra; 35780bca3d3SAxel Dörfler RESTART_PAGE_HEADER *trp; 35880bca3d3SAxel Dörfler int err; 35980bca3d3SAxel Dörfler 36080bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 36180bca3d3SAxel Dörfler /* Check the restart page header for consistency. */ 36280bca3d3SAxel Dörfler if (!ntfs_check_restart_page_header(rp, pos)) { 36380bca3d3SAxel Dörfler /* Error output already done inside the function. */ 36480bca3d3SAxel Dörfler return EINVAL; 36580bca3d3SAxel Dörfler } 36680bca3d3SAxel Dörfler /* Check the restart area for consistency. */ 36780bca3d3SAxel Dörfler if (!ntfs_check_restart_area(rp)) { 36880bca3d3SAxel Dörfler /* Error output already done inside the function. */ 36980bca3d3SAxel Dörfler return EINVAL; 37080bca3d3SAxel Dörfler } 37180bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 37280bca3d3SAxel Dörfler /* 37380bca3d3SAxel Dörfler * Allocate a buffer to store the whole restart page so we can multi 37480bca3d3SAxel Dörfler * sector transfer deprotect it. 37580bca3d3SAxel Dörfler */ 37680bca3d3SAxel Dörfler trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); 37780bca3d3SAxel Dörfler if (!trp) 37880bca3d3SAxel Dörfler return errno; 37980bca3d3SAxel Dörfler /* 38080bca3d3SAxel Dörfler * Read the whole of the restart page into the buffer. If it fits 38180bca3d3SAxel Dörfler * completely inside @rp, just copy it from there. Otherwise read it 38280bca3d3SAxel Dörfler * from disk. 38380bca3d3SAxel Dörfler */ 38480bca3d3SAxel Dörfler if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) 38580bca3d3SAxel Dörfler memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); 38680bca3d3SAxel Dörfler else if (ntfs_attr_pread(log_na, pos, 38780bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size), trp) != 38880bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size)) { 38980bca3d3SAxel Dörfler err = errno; 39080bca3d3SAxel Dörfler ntfs_log_error("Failed to read whole restart page into the " 39180bca3d3SAxel Dörfler "buffer.\n"); 39280bca3d3SAxel Dörfler if (err != ENOMEM) 39380bca3d3SAxel Dörfler err = EIO; 39480bca3d3SAxel Dörfler goto err_out; 39580bca3d3SAxel Dörfler } 39680bca3d3SAxel Dörfler /* 39780bca3d3SAxel Dörfler * Perform the multi sector transfer deprotection on the buffer if the 39880bca3d3SAxel Dörfler * restart page is protected. 39980bca3d3SAxel Dörfler */ 40080bca3d3SAxel Dörfler if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) 40180bca3d3SAxel Dörfler && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, 40280bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size))) { 40380bca3d3SAxel Dörfler /* 40480bca3d3SAxel Dörfler * A multi sector tranfer error was detected. We only need to 40580bca3d3SAxel Dörfler * abort if the restart page contents exceed the multi sector 40680bca3d3SAxel Dörfler * transfer fixup of the first sector. 40780bca3d3SAxel Dörfler */ 40880bca3d3SAxel Dörfler if (le16_to_cpu(rp->restart_area_offset) + 40980bca3d3SAxel Dörfler le16_to_cpu(ra->restart_area_length) > 41080bca3d3SAxel Dörfler NTFS_BLOCK_SIZE - (int)sizeof(u16)) { 41180bca3d3SAxel Dörfler ntfs_log_error("Multi sector transfer error " 41280bca3d3SAxel Dörfler "detected in $LogFile restart page.\n"); 41380bca3d3SAxel Dörfler err = EINVAL; 41480bca3d3SAxel Dörfler goto err_out; 41580bca3d3SAxel Dörfler } 41680bca3d3SAxel Dörfler } 41780bca3d3SAxel Dörfler /* 41880bca3d3SAxel Dörfler * If the restart page is modified by chkdsk or there are no active 41980bca3d3SAxel Dörfler * logfile clients, the logfile is consistent. Otherwise, need to 42080bca3d3SAxel Dörfler * check the log client records for consistency, too. 42180bca3d3SAxel Dörfler */ 42280bca3d3SAxel Dörfler err = 0; 42380bca3d3SAxel Dörfler if (ntfs_is_rstr_record(rp->magic) && 42480bca3d3SAxel Dörfler ra->client_in_use_list != LOGFILE_NO_CLIENT) { 42580bca3d3SAxel Dörfler if (!ntfs_check_log_client_array(trp)) { 42680bca3d3SAxel Dörfler err = EINVAL; 42780bca3d3SAxel Dörfler goto err_out; 42880bca3d3SAxel Dörfler } 42980bca3d3SAxel Dörfler } 43080bca3d3SAxel Dörfler if (lsn) { 43180bca3d3SAxel Dörfler if (ntfs_is_rstr_record(rp->magic)) 43280bca3d3SAxel Dörfler *lsn = sle64_to_cpu(ra->current_lsn); 43380bca3d3SAxel Dörfler else /* if (ntfs_is_chkd_record(rp->magic)) */ 43480bca3d3SAxel Dörfler *lsn = sle64_to_cpu(rp->chkdsk_lsn); 43580bca3d3SAxel Dörfler } 43680bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 43780bca3d3SAxel Dörfler if (wrp) 43880bca3d3SAxel Dörfler *wrp = trp; 43980bca3d3SAxel Dörfler else { 44080bca3d3SAxel Dörfler err_out: 44180bca3d3SAxel Dörfler free(trp); 44280bca3d3SAxel Dörfler } 44380bca3d3SAxel Dörfler return err; 44480bca3d3SAxel Dörfler } 44580bca3d3SAxel Dörfler 44680bca3d3SAxel Dörfler /** 44780bca3d3SAxel Dörfler * ntfs_check_logfile - check in the journal if the volume is consistent 44880bca3d3SAxel Dörfler * @log_na: ntfs attribute of loaded journal $LogFile to check 44980bca3d3SAxel Dörfler * @rp: [OUT] on success this is a copy of the current restart page 45080bca3d3SAxel Dörfler * 45180bca3d3SAxel Dörfler * Check the $LogFile journal for consistency and return TRUE if it is 45280bca3d3SAxel Dörfler * consistent and FALSE if not. On success, the current restart page is 45380bca3d3SAxel Dörfler * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. 45480bca3d3SAxel Dörfler * 45580bca3d3SAxel Dörfler * At present we only check the two restart pages and ignore the log record 45680bca3d3SAxel Dörfler * pages. 45780bca3d3SAxel Dörfler * 45880bca3d3SAxel Dörfler * Note that the MstProtected flag is not set on the $LogFile inode and hence 45980bca3d3SAxel Dörfler * when reading pages they are not deprotected. This is because we do not know 46080bca3d3SAxel Dörfler * if the $LogFile was created on a system with a different page size to ours 46180bca3d3SAxel Dörfler * yet and mst deprotection would fail if our page size is smaller. 46280bca3d3SAxel Dörfler */ 46380bca3d3SAxel Dörfler BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) 46480bca3d3SAxel Dörfler { 46580bca3d3SAxel Dörfler s64 size, pos; 46680bca3d3SAxel Dörfler LSN rstr1_lsn, rstr2_lsn; 46780bca3d3SAxel Dörfler ntfs_volume *vol = log_na->ni->vol; 46880bca3d3SAxel Dörfler u8 *kaddr = NULL; 46980bca3d3SAxel Dörfler RESTART_PAGE_HEADER *rstr1_ph = NULL; 47080bca3d3SAxel Dörfler RESTART_PAGE_HEADER *rstr2_ph = NULL; 47180bca3d3SAxel Dörfler int log_page_size, log_page_mask, err; 47280bca3d3SAxel Dörfler BOOL logfile_is_empty = TRUE; 47380bca3d3SAxel Dörfler u8 log_page_bits; 47480bca3d3SAxel Dörfler 47580bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 47680bca3d3SAxel Dörfler /* An empty $LogFile must have been clean before it got emptied. */ 47780bca3d3SAxel Dörfler if (NVolLogFileEmpty(vol)) 47880bca3d3SAxel Dörfler goto is_empty; 47980bca3d3SAxel Dörfler size = log_na->data_size; 48080bca3d3SAxel Dörfler /* Make sure the file doesn't exceed the maximum allowed size. */ 48180bca3d3SAxel Dörfler if (size > (s64)MaxLogFileSize) 48280bca3d3SAxel Dörfler size = MaxLogFileSize; 48380bca3d3SAxel Dörfler log_page_size = DefaultLogPageSize; 48480bca3d3SAxel Dörfler log_page_mask = log_page_size - 1; 48580bca3d3SAxel Dörfler /* 48680bca3d3SAxel Dörfler * Use generic_ffs() instead of ffs() to enable the compiler to 48780bca3d3SAxel Dörfler * optimize log_page_size and log_page_bits into constants. 48880bca3d3SAxel Dörfler */ 48980bca3d3SAxel Dörfler log_page_bits = ffs(log_page_size) - 1; 49080bca3d3SAxel Dörfler size &= ~(log_page_size - 1); 49180bca3d3SAxel Dörfler 49280bca3d3SAxel Dörfler /* 49380bca3d3SAxel Dörfler * Ensure the log file is big enough to store at least the two restart 49480bca3d3SAxel Dörfler * pages and the minimum number of log record pages. 49580bca3d3SAxel Dörfler */ 49680bca3d3SAxel Dörfler if (size < log_page_size * 2 || (size - log_page_size * 2) >> 49780bca3d3SAxel Dörfler log_page_bits < MinLogRecordPages) { 49880bca3d3SAxel Dörfler ntfs_log_error("$LogFile is too small.\n"); 49980bca3d3SAxel Dörfler return FALSE; 50080bca3d3SAxel Dörfler } 50180bca3d3SAxel Dörfler /* Allocate memory for restart page. */ 50280bca3d3SAxel Dörfler kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); 50380bca3d3SAxel Dörfler if (!kaddr) 50480bca3d3SAxel Dörfler return FALSE; 50580bca3d3SAxel Dörfler /* 50680bca3d3SAxel Dörfler * Read through the file looking for a restart page. Since the restart 50780bca3d3SAxel Dörfler * page header is at the beginning of a page we only need to search at 50880bca3d3SAxel Dörfler * what could be the beginning of a page (for each page size) rather 50980bca3d3SAxel Dörfler * than scanning the whole file byte by byte. If all potential places 51080bca3d3SAxel Dörfler * contain empty and uninitialized records, the log file can be assumed 51180bca3d3SAxel Dörfler * to be empty. 51280bca3d3SAxel Dörfler */ 51380bca3d3SAxel Dörfler for (pos = 0; pos < size; pos <<= 1) { 51480bca3d3SAxel Dörfler /* 51580bca3d3SAxel Dörfler * Read first NTFS_BLOCK_SIZE bytes of potential restart page. 51680bca3d3SAxel Dörfler */ 51780bca3d3SAxel Dörfler if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != 51880bca3d3SAxel Dörfler NTFS_BLOCK_SIZE) { 51980bca3d3SAxel Dörfler ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " 52080bca3d3SAxel Dörfler "bytes of potential restart page.\n"); 52180bca3d3SAxel Dörfler goto err_out; 52280bca3d3SAxel Dörfler } 52380bca3d3SAxel Dörfler 52480bca3d3SAxel Dörfler /* 52580bca3d3SAxel Dörfler * A non-empty block means the logfile is not empty while an 52680bca3d3SAxel Dörfler * empty block after a non-empty block has been encountered 52780bca3d3SAxel Dörfler * means we are done. 52880bca3d3SAxel Dörfler */ 52980bca3d3SAxel Dörfler if (!ntfs_is_empty_recordp((le32*)kaddr)) 53080bca3d3SAxel Dörfler logfile_is_empty = FALSE; 53180bca3d3SAxel Dörfler else if (!logfile_is_empty) 53280bca3d3SAxel Dörfler break; 53380bca3d3SAxel Dörfler /* 53480bca3d3SAxel Dörfler * A log record page means there cannot be a restart page after 53580bca3d3SAxel Dörfler * this so no need to continue searching. 53680bca3d3SAxel Dörfler */ 53780bca3d3SAxel Dörfler if (ntfs_is_rcrd_recordp((le32*)kaddr)) 53880bca3d3SAxel Dörfler break; 53980bca3d3SAxel Dörfler /* If not a (modified by chkdsk) restart page, continue. */ 54080bca3d3SAxel Dörfler if (!ntfs_is_rstr_recordp((le32*)kaddr) && 54180bca3d3SAxel Dörfler !ntfs_is_chkd_recordp((le32*)kaddr)) { 54280bca3d3SAxel Dörfler if (!pos) 54380bca3d3SAxel Dörfler pos = NTFS_BLOCK_SIZE >> 1; 54480bca3d3SAxel Dörfler continue; 54580bca3d3SAxel Dörfler } 54680bca3d3SAxel Dörfler /* 54780bca3d3SAxel Dörfler * Check the (modified by chkdsk) restart page for consistency 54880bca3d3SAxel Dörfler * and get a copy of the complete multi sector transfer 54980bca3d3SAxel Dörfler * deprotected restart page. 55080bca3d3SAxel Dörfler */ 55180bca3d3SAxel Dörfler err = ntfs_check_and_load_restart_page(log_na, 55280bca3d3SAxel Dörfler (RESTART_PAGE_HEADER*)kaddr, pos, 55380bca3d3SAxel Dörfler !rstr1_ph ? &rstr1_ph : &rstr2_ph, 55480bca3d3SAxel Dörfler !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); 55580bca3d3SAxel Dörfler if (!err) { 55680bca3d3SAxel Dörfler /* 55780bca3d3SAxel Dörfler * If we have now found the first (modified by chkdsk) 55880bca3d3SAxel Dörfler * restart page, continue looking for the second one. 55980bca3d3SAxel Dörfler */ 56080bca3d3SAxel Dörfler if (!pos) { 56180bca3d3SAxel Dörfler pos = NTFS_BLOCK_SIZE >> 1; 56280bca3d3SAxel Dörfler continue; 56380bca3d3SAxel Dörfler } 56480bca3d3SAxel Dörfler /* 56580bca3d3SAxel Dörfler * We have now found the second (modified by chkdsk) 56680bca3d3SAxel Dörfler * restart page, so we can stop looking. 56780bca3d3SAxel Dörfler */ 56880bca3d3SAxel Dörfler break; 56980bca3d3SAxel Dörfler } 57080bca3d3SAxel Dörfler /* 57180bca3d3SAxel Dörfler * Error output already done inside the function. Note, we do 57280bca3d3SAxel Dörfler * not abort if the restart page was invalid as we might still 57380bca3d3SAxel Dörfler * find a valid one further in the file. 57480bca3d3SAxel Dörfler */ 57580bca3d3SAxel Dörfler if (err != EINVAL) 57680bca3d3SAxel Dörfler goto err_out; 57780bca3d3SAxel Dörfler /* Continue looking. */ 57880bca3d3SAxel Dörfler if (!pos) 57980bca3d3SAxel Dörfler pos = NTFS_BLOCK_SIZE >> 1; 58080bca3d3SAxel Dörfler } 58180bca3d3SAxel Dörfler if (kaddr) { 58280bca3d3SAxel Dörfler free(kaddr); 58380bca3d3SAxel Dörfler kaddr = NULL; 58480bca3d3SAxel Dörfler } 58580bca3d3SAxel Dörfler if (logfile_is_empty) { 58680bca3d3SAxel Dörfler NVolSetLogFileEmpty(vol); 58780bca3d3SAxel Dörfler is_empty: 58880bca3d3SAxel Dörfler ntfs_log_trace("Done. ($LogFile is empty.)\n"); 58980bca3d3SAxel Dörfler return TRUE; 59080bca3d3SAxel Dörfler } 59180bca3d3SAxel Dörfler if (!rstr1_ph) { 59280bca3d3SAxel Dörfler if (rstr2_ph) 59380bca3d3SAxel Dörfler ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); 59480bca3d3SAxel Dörfler ntfs_log_error("Did not find any restart pages in " 59580bca3d3SAxel Dörfler "$LogFile and it was not empty.\n"); 59680bca3d3SAxel Dörfler return FALSE; 59780bca3d3SAxel Dörfler } 59880bca3d3SAxel Dörfler /* If both restart pages were found, use the more recent one. */ 59980bca3d3SAxel Dörfler if (rstr2_ph) { 60080bca3d3SAxel Dörfler /* 60180bca3d3SAxel Dörfler * If the second restart area is more recent, switch to it. 60280bca3d3SAxel Dörfler * Otherwise just throw it away. 60380bca3d3SAxel Dörfler */ 60480bca3d3SAxel Dörfler if (rstr2_lsn > rstr1_lsn) { 60580bca3d3SAxel Dörfler ntfs_log_debug("Using second restart page as it is more " 60680bca3d3SAxel Dörfler "recent.\n"); 60780bca3d3SAxel Dörfler free(rstr1_ph); 60880bca3d3SAxel Dörfler rstr1_ph = rstr2_ph; 60980bca3d3SAxel Dörfler /* rstr1_lsn = rstr2_lsn; */ 61080bca3d3SAxel Dörfler } else { 61180bca3d3SAxel Dörfler ntfs_log_debug("Using first restart page as it is more " 61280bca3d3SAxel Dörfler "recent.\n"); 61380bca3d3SAxel Dörfler free(rstr2_ph); 61480bca3d3SAxel Dörfler } 61580bca3d3SAxel Dörfler rstr2_ph = NULL; 61680bca3d3SAxel Dörfler } 61780bca3d3SAxel Dörfler /* All consistency checks passed. */ 61880bca3d3SAxel Dörfler if (rp) 61980bca3d3SAxel Dörfler *rp = rstr1_ph; 62080bca3d3SAxel Dörfler else 62180bca3d3SAxel Dörfler free(rstr1_ph); 62280bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 62380bca3d3SAxel Dörfler return TRUE; 62480bca3d3SAxel Dörfler err_out: 62580bca3d3SAxel Dörfler free(kaddr); 62680bca3d3SAxel Dörfler free(rstr1_ph); 62780bca3d3SAxel Dörfler free(rstr2_ph); 62880bca3d3SAxel Dörfler return FALSE; 62980bca3d3SAxel Dörfler } 63080bca3d3SAxel Dörfler 63180bca3d3SAxel Dörfler /** 63280bca3d3SAxel Dörfler * ntfs_is_logfile_clean - check in the journal if the volume is clean 63380bca3d3SAxel Dörfler * @log_na: ntfs attribute of loaded journal $LogFile to check 63480bca3d3SAxel Dörfler * @rp: copy of the current restart page 63580bca3d3SAxel Dörfler * 63680bca3d3SAxel Dörfler * Analyze the $LogFile journal and return TRUE if it indicates the volume was 63780bca3d3SAxel Dörfler * shutdown cleanly and FALSE if not. 63880bca3d3SAxel Dörfler * 63980bca3d3SAxel Dörfler * At present we only look at the two restart pages and ignore the log record 64080bca3d3SAxel Dörfler * pages. This is a little bit crude in that there will be a very small number 64180bca3d3SAxel Dörfler * of cases where we think that a volume is dirty when in fact it is clean. 64280bca3d3SAxel Dörfler * This should only affect volumes that have not been shutdown cleanly but did 64380bca3d3SAxel Dörfler * not have any pending, non-check-pointed i/o, i.e. they were completely idle 64480bca3d3SAxel Dörfler * at least for the five seconds preceding the unclean shutdown. 64580bca3d3SAxel Dörfler * 64680bca3d3SAxel Dörfler * This function assumes that the $LogFile journal has already been consistency 64780bca3d3SAxel Dörfler * checked by a call to ntfs_check_logfile() and in particular if the $LogFile 64880bca3d3SAxel Dörfler * is empty this function requires that NVolLogFileEmpty() is true otherwise an 64980bca3d3SAxel Dörfler * empty volume will be reported as dirty. 65080bca3d3SAxel Dörfler */ 65180bca3d3SAxel Dörfler BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) 65280bca3d3SAxel Dörfler { 65380bca3d3SAxel Dörfler RESTART_AREA *ra; 65480bca3d3SAxel Dörfler 65580bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 65680bca3d3SAxel Dörfler /* An empty $LogFile must have been clean before it got emptied. */ 65780bca3d3SAxel Dörfler if (NVolLogFileEmpty(log_na->ni->vol)) { 6586b3592caSGerasim Troeglazov ntfs_log_trace("$LogFile is empty\n"); 65980bca3d3SAxel Dörfler return TRUE; 66080bca3d3SAxel Dörfler } 66180bca3d3SAxel Dörfler if (!rp) { 6626b3592caSGerasim Troeglazov ntfs_log_error("Restart page header is NULL\n"); 66380bca3d3SAxel Dörfler return FALSE; 66480bca3d3SAxel Dörfler } 66580bca3d3SAxel Dörfler if (!ntfs_is_rstr_record(rp->magic) && 66680bca3d3SAxel Dörfler !ntfs_is_chkd_record(rp->magic)) { 6676b3592caSGerasim Troeglazov ntfs_log_error("Restart page buffer is invalid\n"); 66880bca3d3SAxel Dörfler return FALSE; 66980bca3d3SAxel Dörfler } 67080bca3d3SAxel Dörfler 67180bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 67280bca3d3SAxel Dörfler /* 67380bca3d3SAxel Dörfler * If the $LogFile has active clients, i.e. it is open, and we do not 67480bca3d3SAxel Dörfler * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, 67580bca3d3SAxel Dörfler * we assume there was an unclean shutdown. 67680bca3d3SAxel Dörfler */ 67780bca3d3SAxel Dörfler if (ra->client_in_use_list != LOGFILE_NO_CLIENT && 67880bca3d3SAxel Dörfler !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { 6796b3592caSGerasim Troeglazov ntfs_log_error("$LogFile indicates unclean shutdown (%d, %d)\n", 6806b3592caSGerasim Troeglazov le16_to_cpu(ra->client_in_use_list), 6816b3592caSGerasim Troeglazov le16_to_cpu(ra->flags)); 68280bca3d3SAxel Dörfler return FALSE; 68380bca3d3SAxel Dörfler } 68480bca3d3SAxel Dörfler /* $LogFile indicates a clean shutdown. */ 6856b3592caSGerasim Troeglazov ntfs_log_trace("$LogFile indicates a clean shutdown\n"); 68680bca3d3SAxel Dörfler return TRUE; 68780bca3d3SAxel Dörfler } 68880bca3d3SAxel Dörfler 68980bca3d3SAxel Dörfler /** 69080bca3d3SAxel Dörfler * ntfs_empty_logfile - empty the contents of the $LogFile journal 69180bca3d3SAxel Dörfler * @na: ntfs attribute of journal $LogFile to empty 69280bca3d3SAxel Dörfler * 69380bca3d3SAxel Dörfler * Empty the contents of the $LogFile journal @na and return 0 on success and 69480bca3d3SAxel Dörfler * -1 on error. 69580bca3d3SAxel Dörfler * 69680bca3d3SAxel Dörfler * This function assumes that the $LogFile journal has already been consistency 69780bca3d3SAxel Dörfler * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() 69880bca3d3SAxel Dörfler * has been used to ensure that the $LogFile is clean. 69980bca3d3SAxel Dörfler */ 70080bca3d3SAxel Dörfler int ntfs_empty_logfile(ntfs_attr *na) 70180bca3d3SAxel Dörfler { 702*62cee9f9SGerasim Troeglazov s64 pos, count; 70380bca3d3SAxel Dörfler char buf[NTFS_BUF_SIZE]; 70480bca3d3SAxel Dörfler 70580bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 706*62cee9f9SGerasim Troeglazov 70780bca3d3SAxel Dörfler if (NVolLogFileEmpty(na->ni->vol)) 70880bca3d3SAxel Dörfler return 0; 70980bca3d3SAxel Dörfler 71080bca3d3SAxel Dörfler if (!NAttrNonResident(na)) { 71180bca3d3SAxel Dörfler errno = EIO; 712*62cee9f9SGerasim Troeglazov ntfs_log_perror("Resident $LogFile $DATA attribute"); 71380bca3d3SAxel Dörfler return -1; 71480bca3d3SAxel Dörfler } 71580bca3d3SAxel Dörfler 71680bca3d3SAxel Dörfler memset(buf, -1, NTFS_BUF_SIZE); 71780bca3d3SAxel Dörfler 71880bca3d3SAxel Dörfler pos = 0; 719*62cee9f9SGerasim Troeglazov while ((count = na->data_size - pos) > 0) { 720*62cee9f9SGerasim Troeglazov 72180bca3d3SAxel Dörfler if (count > NTFS_BUF_SIZE) 72280bca3d3SAxel Dörfler count = NTFS_BUF_SIZE; 72380bca3d3SAxel Dörfler 724*62cee9f9SGerasim Troeglazov count = ntfs_attr_pwrite(na, pos, count, buf); 725*62cee9f9SGerasim Troeglazov if (count <= 0) { 726*62cee9f9SGerasim Troeglazov ntfs_log_perror("Failed to reset $LogFile"); 72780bca3d3SAxel Dörfler if (count != -1) 72880bca3d3SAxel Dörfler errno = EIO; 72980bca3d3SAxel Dörfler return -1; 73080bca3d3SAxel Dörfler } 73180bca3d3SAxel Dörfler pos += count; 73280bca3d3SAxel Dörfler } 73380bca3d3SAxel Dörfler 73480bca3d3SAxel Dörfler NVolSetLogFileEmpty(na->ni->vol); 735*62cee9f9SGerasim Troeglazov 73680bca3d3SAxel Dörfler return 0; 73780bca3d3SAxel Dörfler } 738