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 630bc84e9SGerasim Troeglazov * Copyright (c) 2005-2009 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 } 87*da0906f2SGerasim Troeglazov /* 88*da0906f2SGerasim Troeglazov * We only know how to handle version 1.1 and 2.0, though 89*da0906f2SGerasim Troeglazov * version 2.0 is probably related to cached metadata in 90*da0906f2SGerasim Troeglazov * Windows 8, and we will refuse to mount. 91*da0906f2SGerasim Troeglazov * Nevertheless, do all the relevant checks before rejecting. 92*da0906f2SGerasim Troeglazov */ 93*da0906f2SGerasim Troeglazov if (((rp->major_ver != const_cpu_to_le16(1)) 94*da0906f2SGerasim Troeglazov || (rp->minor_ver != const_cpu_to_le16(1))) 95*da0906f2SGerasim Troeglazov && ((rp->major_ver != const_cpu_to_le16(2)) 96*da0906f2SGerasim Troeglazov || (rp->minor_ver != const_cpu_to_le16(0)))) { 9780bca3d3SAxel Dörfler ntfs_log_error("$LogFile version %i.%i is not " 98*da0906f2SGerasim Troeglazov "supported.\n (This driver supports version " 99*da0906f2SGerasim Troeglazov "1.1 and 2.0 only.)\n", 100*da0906f2SGerasim Troeglazov (int)sle16_to_cpu(rp->major_ver), 10180bca3d3SAxel Dörfler (int)sle16_to_cpu(rp->minor_ver)); 10280bca3d3SAxel Dörfler return FALSE; 10380bca3d3SAxel Dörfler } 10480bca3d3SAxel Dörfler /* 10580bca3d3SAxel Dörfler * If chkdsk has been run the restart page may not be protected by an 10680bca3d3SAxel Dörfler * update sequence array. 10780bca3d3SAxel Dörfler */ 10880bca3d3SAxel Dörfler if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { 10980bca3d3SAxel Dörfler have_usa = FALSE; 11080bca3d3SAxel Dörfler goto skip_usa_checks; 11180bca3d3SAxel Dörfler } 11280bca3d3SAxel Dörfler /* Verify the size of the update sequence array. */ 11380bca3d3SAxel Dörfler usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); 11480bca3d3SAxel Dörfler if (usa_count != le16_to_cpu(rp->usa_count)) { 11580bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page specifies " 11680bca3d3SAxel Dörfler "inconsistent update sequence array count.\n"); 11780bca3d3SAxel Dörfler return FALSE; 11880bca3d3SAxel Dörfler } 11980bca3d3SAxel Dörfler /* Verify the position of the update sequence array. */ 12080bca3d3SAxel Dörfler usa_ofs = le16_to_cpu(rp->usa_ofs); 12180bca3d3SAxel Dörfler usa_end = usa_ofs + usa_count * sizeof(u16); 12280bca3d3SAxel Dörfler if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || 12380bca3d3SAxel Dörfler usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { 12480bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page specifies " 12580bca3d3SAxel Dörfler "inconsistent update sequence array offset.\n"); 12680bca3d3SAxel Dörfler return FALSE; 12780bca3d3SAxel Dörfler } 12880bca3d3SAxel Dörfler skip_usa_checks: 12980bca3d3SAxel Dörfler /* 13080bca3d3SAxel Dörfler * Verify the position of the restart area. It must be: 13180bca3d3SAxel Dörfler * - aligned to 8-byte boundary, 13280bca3d3SAxel Dörfler * - after the update sequence array, and 13380bca3d3SAxel Dörfler * - within the system page size. 13480bca3d3SAxel Dörfler */ 13580bca3d3SAxel Dörfler ra_ofs = le16_to_cpu(rp->restart_area_offset); 13680bca3d3SAxel Dörfler if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : 13780bca3d3SAxel Dörfler ra_ofs < sizeof(RESTART_PAGE_HEADER)) || 13880bca3d3SAxel Dörfler ra_ofs > logfile_system_page_size) { 13980bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page specifies " 14080bca3d3SAxel Dörfler "inconsistent restart area offset.\n"); 14180bca3d3SAxel Dörfler return FALSE; 14280bca3d3SAxel Dörfler } 14380bca3d3SAxel Dörfler /* 14480bca3d3SAxel Dörfler * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn 14580bca3d3SAxel Dörfler * set. 14680bca3d3SAxel Dörfler */ 14780bca3d3SAxel Dörfler if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { 14880bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart page is not modified " 14980bca3d3SAxel Dörfler "by chkdsk but a chkdsk LSN is specified.\n"); 15080bca3d3SAxel Dörfler return FALSE; 15180bca3d3SAxel Dörfler } 15280bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 15380bca3d3SAxel Dörfler return TRUE; 15480bca3d3SAxel Dörfler } 15580bca3d3SAxel Dörfler 15680bca3d3SAxel Dörfler /** 15780bca3d3SAxel Dörfler * ntfs_check_restart_area - check the restart area for consistency 15880bca3d3SAxel Dörfler * @rp: restart page whose restart area to check 15980bca3d3SAxel Dörfler * 16080bca3d3SAxel Dörfler * Check the restart area of the restart page @rp for consistency and return 16180bca3d3SAxel Dörfler * TRUE if it is consistent and FALSE otherwise. 16280bca3d3SAxel Dörfler * 16380bca3d3SAxel Dörfler * This function assumes that the restart page header has already been 16480bca3d3SAxel Dörfler * consistency checked. 16580bca3d3SAxel Dörfler * 16680bca3d3SAxel Dörfler * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 16780bca3d3SAxel Dörfler * require the full restart page. 16880bca3d3SAxel Dörfler */ 16980bca3d3SAxel Dörfler static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) 17080bca3d3SAxel Dörfler { 17180bca3d3SAxel Dörfler u64 file_size; 17280bca3d3SAxel Dörfler RESTART_AREA *ra; 17380bca3d3SAxel Dörfler u16 ra_ofs, ra_len, ca_ofs; 17480bca3d3SAxel Dörfler u8 fs_bits; 17580bca3d3SAxel Dörfler 17680bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 17780bca3d3SAxel Dörfler ra_ofs = le16_to_cpu(rp->restart_area_offset); 17880bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + ra_ofs); 17980bca3d3SAxel Dörfler /* 18080bca3d3SAxel Dörfler * Everything before ra->file_size must be before the first word 18180bca3d3SAxel Dörfler * protected by an update sequence number. This ensures that it is 18280bca3d3SAxel Dörfler * safe to access ra->client_array_offset. 18380bca3d3SAxel Dörfler */ 18480bca3d3SAxel Dörfler if (ra_ofs + offsetof(RESTART_AREA, file_size) > 18580bca3d3SAxel Dörfler NTFS_BLOCK_SIZE - sizeof(u16)) { 18680bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 18780bca3d3SAxel Dörfler "inconsistent file offset.\n"); 18880bca3d3SAxel Dörfler return FALSE; 18980bca3d3SAxel Dörfler } 19080bca3d3SAxel Dörfler /* 19180bca3d3SAxel Dörfler * Now that we can access ra->client_array_offset, make sure everything 19280bca3d3SAxel Dörfler * up to the log client array is before the first word protected by an 19380bca3d3SAxel Dörfler * update sequence number. This ensures we can access all of the 19480bca3d3SAxel Dörfler * restart area elements safely. Also, the client array offset must be 19580bca3d3SAxel Dörfler * aligned to an 8-byte boundary. 19680bca3d3SAxel Dörfler */ 19780bca3d3SAxel Dörfler ca_ofs = le16_to_cpu(ra->client_array_offset); 19880bca3d3SAxel Dörfler if (((ca_ofs + 7) & ~7) != ca_ofs || 19980bca3d3SAxel Dörfler ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - 20080bca3d3SAxel Dörfler sizeof(u16))) { 20180bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 20280bca3d3SAxel Dörfler "inconsistent client array offset.\n"); 20380bca3d3SAxel Dörfler return FALSE; 20480bca3d3SAxel Dörfler } 20580bca3d3SAxel Dörfler /* 20680bca3d3SAxel Dörfler * The restart area must end within the system page size both when 20780bca3d3SAxel Dörfler * calculated manually and as specified by ra->restart_area_length. 20880bca3d3SAxel Dörfler * Also, the calculated length must not exceed the specified length. 20980bca3d3SAxel Dörfler */ 21080bca3d3SAxel Dörfler ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * 21180bca3d3SAxel Dörfler sizeof(LOG_CLIENT_RECORD); 21280bca3d3SAxel Dörfler if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || 21380bca3d3SAxel Dörfler (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > 21480bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size) || 21580bca3d3SAxel Dörfler ra_len > le16_to_cpu(ra->restart_area_length)) { 21680bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area is out of bounds " 21780bca3d3SAxel Dörfler "of the system page size specified by the " 21880bca3d3SAxel Dörfler "restart page header and/or the specified " 21980bca3d3SAxel Dörfler "restart area length is inconsistent.\n"); 22080bca3d3SAxel Dörfler return FALSE; 22180bca3d3SAxel Dörfler } 22280bca3d3SAxel Dörfler /* 22380bca3d3SAxel Dörfler * The ra->client_free_list and ra->client_in_use_list must be either 22480bca3d3SAxel Dörfler * LOGFILE_NO_CLIENT or less than ra->log_clients or they are 22580bca3d3SAxel Dörfler * overflowing the client array. 22680bca3d3SAxel Dörfler */ 22780bca3d3SAxel Dörfler if ((ra->client_free_list != LOGFILE_NO_CLIENT && 22880bca3d3SAxel Dörfler le16_to_cpu(ra->client_free_list) >= 22980bca3d3SAxel Dörfler le16_to_cpu(ra->log_clients)) || 23080bca3d3SAxel Dörfler (ra->client_in_use_list != LOGFILE_NO_CLIENT && 23180bca3d3SAxel Dörfler le16_to_cpu(ra->client_in_use_list) >= 23280bca3d3SAxel Dörfler le16_to_cpu(ra->log_clients))) { 23380bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 23480bca3d3SAxel Dörfler "overflowing client free and/or in use lists.\n"); 23580bca3d3SAxel Dörfler return FALSE; 23680bca3d3SAxel Dörfler } 23780bca3d3SAxel Dörfler /* 23880bca3d3SAxel Dörfler * Check ra->seq_number_bits against ra->file_size for consistency. 23980bca3d3SAxel Dörfler * We cannot just use ffs() because the file size is not a power of 2. 24080bca3d3SAxel Dörfler */ 24180bca3d3SAxel Dörfler file_size = (u64)sle64_to_cpu(ra->file_size); 24280bca3d3SAxel Dörfler fs_bits = 0; 24380bca3d3SAxel Dörfler while (file_size) { 24480bca3d3SAxel Dörfler file_size >>= 1; 24580bca3d3SAxel Dörfler fs_bits++; 24680bca3d3SAxel Dörfler } 24780bca3d3SAxel Dörfler if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { 24880bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 24980bca3d3SAxel Dörfler "inconsistent sequence number bits.\n"); 25080bca3d3SAxel Dörfler return FALSE; 25180bca3d3SAxel Dörfler } 25280bca3d3SAxel Dörfler /* The log record header length must be a multiple of 8. */ 25380bca3d3SAxel Dörfler if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != 25480bca3d3SAxel Dörfler le16_to_cpu(ra->log_record_header_length)) { 25580bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 25680bca3d3SAxel Dörfler "inconsistent log record header length.\n"); 25780bca3d3SAxel Dörfler return FALSE; 25880bca3d3SAxel Dörfler } 25980bca3d3SAxel Dörfler /* Ditto for the log page data offset. */ 26080bca3d3SAxel Dörfler if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != 26180bca3d3SAxel Dörfler le16_to_cpu(ra->log_page_data_offset)) { 26280bca3d3SAxel Dörfler ntfs_log_error("$LogFile restart area specifies " 26380bca3d3SAxel Dörfler "inconsistent log page data offset.\n"); 26480bca3d3SAxel Dörfler return FALSE; 26580bca3d3SAxel Dörfler } 26680bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 26780bca3d3SAxel Dörfler return TRUE; 26880bca3d3SAxel Dörfler } 26980bca3d3SAxel Dörfler 27080bca3d3SAxel Dörfler /** 27180bca3d3SAxel Dörfler * ntfs_check_log_client_array - check the log client array for consistency 27280bca3d3SAxel Dörfler * @rp: restart page whose log client array to check 27380bca3d3SAxel Dörfler * 27480bca3d3SAxel Dörfler * Check the log client array of the restart page @rp for consistency and 27580bca3d3SAxel Dörfler * return TRUE if it is consistent and FALSE otherwise. 27680bca3d3SAxel Dörfler * 27780bca3d3SAxel Dörfler * This function assumes that the restart page header and the restart area have 27880bca3d3SAxel Dörfler * already been consistency checked. 27980bca3d3SAxel Dörfler * 28080bca3d3SAxel Dörfler * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this 28180bca3d3SAxel Dörfler * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full 28280bca3d3SAxel Dörfler * restart page and the page must be multi sector transfer deprotected. 28380bca3d3SAxel Dörfler */ 28480bca3d3SAxel Dörfler static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) 28580bca3d3SAxel Dörfler { 28680bca3d3SAxel Dörfler RESTART_AREA *ra; 28780bca3d3SAxel Dörfler LOG_CLIENT_RECORD *ca, *cr; 28880bca3d3SAxel Dörfler u16 nr_clients, idx; 28980bca3d3SAxel Dörfler BOOL in_free_list, idx_is_first; 29080bca3d3SAxel Dörfler 29180bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 29280bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 29380bca3d3SAxel Dörfler ca = (LOG_CLIENT_RECORD*)((u8*)ra + 29480bca3d3SAxel Dörfler le16_to_cpu(ra->client_array_offset)); 29580bca3d3SAxel Dörfler /* 29680bca3d3SAxel Dörfler * Check the ra->client_free_list first and then check the 29780bca3d3SAxel Dörfler * ra->client_in_use_list. Check each of the log client records in 29880bca3d3SAxel Dörfler * each of the lists and check that the array does not overflow the 29980bca3d3SAxel Dörfler * ra->log_clients value. Also keep track of the number of records 30080bca3d3SAxel Dörfler * visited as there cannot be more than ra->log_clients records and 30180bca3d3SAxel Dörfler * that way we detect eventual loops in within a list. 30280bca3d3SAxel Dörfler */ 30380bca3d3SAxel Dörfler nr_clients = le16_to_cpu(ra->log_clients); 30480bca3d3SAxel Dörfler idx = le16_to_cpu(ra->client_free_list); 30580bca3d3SAxel Dörfler in_free_list = TRUE; 30680bca3d3SAxel Dörfler check_list: 30780bca3d3SAxel Dörfler for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, 30880bca3d3SAxel Dörfler idx = le16_to_cpu(cr->next_client)) { 30980bca3d3SAxel Dörfler if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) 31080bca3d3SAxel Dörfler goto err_out; 31180bca3d3SAxel Dörfler /* Set @cr to the current log client record. */ 31280bca3d3SAxel Dörfler cr = ca + idx; 31380bca3d3SAxel Dörfler /* The first log client record must not have a prev_client. */ 31480bca3d3SAxel Dörfler if (idx_is_first) { 31580bca3d3SAxel Dörfler if (cr->prev_client != LOGFILE_NO_CLIENT) 31680bca3d3SAxel Dörfler goto err_out; 31780bca3d3SAxel Dörfler idx_is_first = FALSE; 31880bca3d3SAxel Dörfler } 31980bca3d3SAxel Dörfler } 32080bca3d3SAxel Dörfler /* Switch to and check the in use list if we just did the free list. */ 32180bca3d3SAxel Dörfler if (in_free_list) { 32280bca3d3SAxel Dörfler in_free_list = FALSE; 32380bca3d3SAxel Dörfler idx = le16_to_cpu(ra->client_in_use_list); 32480bca3d3SAxel Dörfler goto check_list; 32580bca3d3SAxel Dörfler } 32680bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 32780bca3d3SAxel Dörfler return TRUE; 32880bca3d3SAxel Dörfler err_out: 32980bca3d3SAxel Dörfler ntfs_log_error("$LogFile log client array is corrupt.\n"); 33080bca3d3SAxel Dörfler return FALSE; 33180bca3d3SAxel Dörfler } 33280bca3d3SAxel Dörfler 33380bca3d3SAxel Dörfler /** 33480bca3d3SAxel Dörfler * ntfs_check_and_load_restart_page - check the restart page for consistency 33580bca3d3SAxel Dörfler * @log_na: opened ntfs attribute for journal $LogFile 33680bca3d3SAxel Dörfler * @rp: restart page to check 33780bca3d3SAxel Dörfler * @pos: position in @log_na at which the restart page resides 33880bca3d3SAxel Dörfler * @wrp: [OUT] copy of the multi sector transfer deprotected restart page 33980bca3d3SAxel Dörfler * @lsn: [OUT] set to the current logfile lsn on success 34080bca3d3SAxel Dörfler * 34180bca3d3SAxel Dörfler * Check the restart page @rp for consistency and return 0 if it is consistent 34280bca3d3SAxel Dörfler * and errno otherwise. The restart page may have been modified by chkdsk in 34380bca3d3SAxel Dörfler * which case its magic is CHKD instead of RSTR. 34480bca3d3SAxel Dörfler * 34580bca3d3SAxel Dörfler * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 34680bca3d3SAxel Dörfler * require the full restart page. 34780bca3d3SAxel Dörfler * 34880bca3d3SAxel Dörfler * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a 34980bca3d3SAxel Dörfler * copy of the complete multi sector transfer deprotected page. On failure, 35080bca3d3SAxel Dörfler * *@wrp is undefined. 35180bca3d3SAxel Dörfler * 35280bca3d3SAxel Dörfler * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current 35380bca3d3SAxel Dörfler * logfile lsn according to this restart page. On failure, *@lsn is undefined. 35480bca3d3SAxel Dörfler * 35580bca3d3SAxel Dörfler * The following error codes are defined: 35680bca3d3SAxel Dörfler * EINVAL - The restart page is inconsistent. 35780bca3d3SAxel Dörfler * ENOMEM - Not enough memory to load the restart page. 35880bca3d3SAxel Dörfler * EIO - Failed to reading from $LogFile. 35980bca3d3SAxel Dörfler */ 36080bca3d3SAxel Dörfler static int ntfs_check_and_load_restart_page(ntfs_attr *log_na, 36180bca3d3SAxel Dörfler RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, 36280bca3d3SAxel Dörfler LSN *lsn) 36380bca3d3SAxel Dörfler { 36480bca3d3SAxel Dörfler RESTART_AREA *ra; 36580bca3d3SAxel Dörfler RESTART_PAGE_HEADER *trp; 36680bca3d3SAxel Dörfler int err; 36780bca3d3SAxel Dörfler 36880bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 36980bca3d3SAxel Dörfler /* Check the restart page header for consistency. */ 37080bca3d3SAxel Dörfler if (!ntfs_check_restart_page_header(rp, pos)) { 37180bca3d3SAxel Dörfler /* Error output already done inside the function. */ 37280bca3d3SAxel Dörfler return EINVAL; 37380bca3d3SAxel Dörfler } 37480bca3d3SAxel Dörfler /* Check the restart area for consistency. */ 37580bca3d3SAxel Dörfler if (!ntfs_check_restart_area(rp)) { 37680bca3d3SAxel Dörfler /* Error output already done inside the function. */ 37780bca3d3SAxel Dörfler return EINVAL; 37880bca3d3SAxel Dörfler } 37980bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 38080bca3d3SAxel Dörfler /* 38180bca3d3SAxel Dörfler * Allocate a buffer to store the whole restart page so we can multi 38280bca3d3SAxel Dörfler * sector transfer deprotect it. 38380bca3d3SAxel Dörfler */ 38480bca3d3SAxel Dörfler trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); 38580bca3d3SAxel Dörfler if (!trp) 38680bca3d3SAxel Dörfler return errno; 38780bca3d3SAxel Dörfler /* 38880bca3d3SAxel Dörfler * Read the whole of the restart page into the buffer. If it fits 38980bca3d3SAxel Dörfler * completely inside @rp, just copy it from there. Otherwise read it 39080bca3d3SAxel Dörfler * from disk. 39180bca3d3SAxel Dörfler */ 39280bca3d3SAxel Dörfler if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) 39380bca3d3SAxel Dörfler memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); 39480bca3d3SAxel Dörfler else if (ntfs_attr_pread(log_na, pos, 39580bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size), trp) != 39680bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size)) { 39780bca3d3SAxel Dörfler err = errno; 39880bca3d3SAxel Dörfler ntfs_log_error("Failed to read whole restart page into the " 39980bca3d3SAxel Dörfler "buffer.\n"); 40080bca3d3SAxel Dörfler if (err != ENOMEM) 40180bca3d3SAxel Dörfler err = EIO; 40280bca3d3SAxel Dörfler goto err_out; 40380bca3d3SAxel Dörfler } 40480bca3d3SAxel Dörfler /* 40580bca3d3SAxel Dörfler * Perform the multi sector transfer deprotection on the buffer if the 40680bca3d3SAxel Dörfler * restart page is protected. 40780bca3d3SAxel Dörfler */ 40880bca3d3SAxel Dörfler if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) 40980bca3d3SAxel Dörfler && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, 41080bca3d3SAxel Dörfler le32_to_cpu(rp->system_page_size))) { 41180bca3d3SAxel Dörfler /* 41280bca3d3SAxel Dörfler * A multi sector tranfer error was detected. We only need to 41380bca3d3SAxel Dörfler * abort if the restart page contents exceed the multi sector 41480bca3d3SAxel Dörfler * transfer fixup of the first sector. 41580bca3d3SAxel Dörfler */ 41680bca3d3SAxel Dörfler if (le16_to_cpu(rp->restart_area_offset) + 41780bca3d3SAxel Dörfler le16_to_cpu(ra->restart_area_length) > 41880bca3d3SAxel Dörfler NTFS_BLOCK_SIZE - (int)sizeof(u16)) { 41980bca3d3SAxel Dörfler ntfs_log_error("Multi sector transfer error " 42080bca3d3SAxel Dörfler "detected in $LogFile restart page.\n"); 42180bca3d3SAxel Dörfler err = EINVAL; 42280bca3d3SAxel Dörfler goto err_out; 42380bca3d3SAxel Dörfler } 42480bca3d3SAxel Dörfler } 42580bca3d3SAxel Dörfler /* 42680bca3d3SAxel Dörfler * If the restart page is modified by chkdsk or there are no active 42780bca3d3SAxel Dörfler * logfile clients, the logfile is consistent. Otherwise, need to 42880bca3d3SAxel Dörfler * check the log client records for consistency, too. 42980bca3d3SAxel Dörfler */ 43080bca3d3SAxel Dörfler err = 0; 43180bca3d3SAxel Dörfler if (ntfs_is_rstr_record(rp->magic) && 43280bca3d3SAxel Dörfler ra->client_in_use_list != LOGFILE_NO_CLIENT) { 43380bca3d3SAxel Dörfler if (!ntfs_check_log_client_array(trp)) { 43480bca3d3SAxel Dörfler err = EINVAL; 43580bca3d3SAxel Dörfler goto err_out; 43680bca3d3SAxel Dörfler } 43780bca3d3SAxel Dörfler } 43880bca3d3SAxel Dörfler if (lsn) { 43980bca3d3SAxel Dörfler if (ntfs_is_rstr_record(rp->magic)) 44080bca3d3SAxel Dörfler *lsn = sle64_to_cpu(ra->current_lsn); 44180bca3d3SAxel Dörfler else /* if (ntfs_is_chkd_record(rp->magic)) */ 44280bca3d3SAxel Dörfler *lsn = sle64_to_cpu(rp->chkdsk_lsn); 44380bca3d3SAxel Dörfler } 44480bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 44580bca3d3SAxel Dörfler if (wrp) 44680bca3d3SAxel Dörfler *wrp = trp; 44780bca3d3SAxel Dörfler else { 44880bca3d3SAxel Dörfler err_out: 44980bca3d3SAxel Dörfler free(trp); 45080bca3d3SAxel Dörfler } 45180bca3d3SAxel Dörfler return err; 45280bca3d3SAxel Dörfler } 45380bca3d3SAxel Dörfler 45480bca3d3SAxel Dörfler /** 45580bca3d3SAxel Dörfler * ntfs_check_logfile - check in the journal if the volume is consistent 45680bca3d3SAxel Dörfler * @log_na: ntfs attribute of loaded journal $LogFile to check 45780bca3d3SAxel Dörfler * @rp: [OUT] on success this is a copy of the current restart page 45880bca3d3SAxel Dörfler * 45980bca3d3SAxel Dörfler * Check the $LogFile journal for consistency and return TRUE if it is 46080bca3d3SAxel Dörfler * consistent and FALSE if not. On success, the current restart page is 46180bca3d3SAxel Dörfler * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. 46280bca3d3SAxel Dörfler * 46380bca3d3SAxel Dörfler * At present we only check the two restart pages and ignore the log record 46480bca3d3SAxel Dörfler * pages. 46580bca3d3SAxel Dörfler * 46680bca3d3SAxel Dörfler * Note that the MstProtected flag is not set on the $LogFile inode and hence 46780bca3d3SAxel Dörfler * when reading pages they are not deprotected. This is because we do not know 46880bca3d3SAxel Dörfler * if the $LogFile was created on a system with a different page size to ours 46980bca3d3SAxel Dörfler * yet and mst deprotection would fail if our page size is smaller. 47080bca3d3SAxel Dörfler */ 47180bca3d3SAxel Dörfler BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) 47280bca3d3SAxel Dörfler { 47380bca3d3SAxel Dörfler s64 size, pos; 47480bca3d3SAxel Dörfler LSN rstr1_lsn, rstr2_lsn; 47580bca3d3SAxel Dörfler ntfs_volume *vol = log_na->ni->vol; 47680bca3d3SAxel Dörfler u8 *kaddr = NULL; 47780bca3d3SAxel Dörfler RESTART_PAGE_HEADER *rstr1_ph = NULL; 47880bca3d3SAxel Dörfler RESTART_PAGE_HEADER *rstr2_ph = NULL; 479a814d850Sthreedeyes int log_page_size, err; 48080bca3d3SAxel Dörfler BOOL logfile_is_empty = TRUE; 48180bca3d3SAxel Dörfler u8 log_page_bits; 48280bca3d3SAxel Dörfler 48380bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 48480bca3d3SAxel Dörfler /* An empty $LogFile must have been clean before it got emptied. */ 48580bca3d3SAxel Dörfler if (NVolLogFileEmpty(vol)) 48680bca3d3SAxel Dörfler goto is_empty; 48780bca3d3SAxel Dörfler size = log_na->data_size; 48880bca3d3SAxel Dörfler /* Make sure the file doesn't exceed the maximum allowed size. */ 48980bca3d3SAxel Dörfler if (size > (s64)MaxLogFileSize) 49080bca3d3SAxel Dörfler size = MaxLogFileSize; 49180bca3d3SAxel Dörfler log_page_size = DefaultLogPageSize; 49280bca3d3SAxel Dörfler /* 49380bca3d3SAxel Dörfler * Use generic_ffs() instead of ffs() to enable the compiler to 49480bca3d3SAxel Dörfler * optimize log_page_size and log_page_bits into constants. 49580bca3d3SAxel Dörfler */ 49680bca3d3SAxel Dörfler log_page_bits = ffs(log_page_size) - 1; 49780bca3d3SAxel Dörfler size &= ~(log_page_size - 1); 49880bca3d3SAxel Dörfler 49980bca3d3SAxel Dörfler /* 50080bca3d3SAxel Dörfler * Ensure the log file is big enough to store at least the two restart 50180bca3d3SAxel Dörfler * pages and the minimum number of log record pages. 50280bca3d3SAxel Dörfler */ 50380bca3d3SAxel Dörfler if (size < log_page_size * 2 || (size - log_page_size * 2) >> 50480bca3d3SAxel Dörfler log_page_bits < MinLogRecordPages) { 50580bca3d3SAxel Dörfler ntfs_log_error("$LogFile is too small.\n"); 50680bca3d3SAxel Dörfler return FALSE; 50780bca3d3SAxel Dörfler } 50880bca3d3SAxel Dörfler /* Allocate memory for restart page. */ 50980bca3d3SAxel Dörfler kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); 51080bca3d3SAxel Dörfler if (!kaddr) 51180bca3d3SAxel Dörfler return FALSE; 51280bca3d3SAxel Dörfler /* 51380bca3d3SAxel Dörfler * Read through the file looking for a restart page. Since the restart 51480bca3d3SAxel Dörfler * page header is at the beginning of a page we only need to search at 51580bca3d3SAxel Dörfler * what could be the beginning of a page (for each page size) rather 51680bca3d3SAxel Dörfler * than scanning the whole file byte by byte. If all potential places 51780bca3d3SAxel Dörfler * contain empty and uninitialized records, the log file can be assumed 51880bca3d3SAxel Dörfler * to be empty. 51980bca3d3SAxel Dörfler */ 52080bca3d3SAxel Dörfler for (pos = 0; pos < size; pos <<= 1) { 52180bca3d3SAxel Dörfler /* 52280bca3d3SAxel Dörfler * Read first NTFS_BLOCK_SIZE bytes of potential restart page. 52380bca3d3SAxel Dörfler */ 52480bca3d3SAxel Dörfler if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != 52580bca3d3SAxel Dörfler NTFS_BLOCK_SIZE) { 52680bca3d3SAxel Dörfler ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " 52780bca3d3SAxel Dörfler "bytes of potential restart page.\n"); 52880bca3d3SAxel Dörfler goto err_out; 52980bca3d3SAxel Dörfler } 53080bca3d3SAxel Dörfler 53180bca3d3SAxel Dörfler /* 53280bca3d3SAxel Dörfler * A non-empty block means the logfile is not empty while an 53380bca3d3SAxel Dörfler * empty block after a non-empty block has been encountered 53480bca3d3SAxel Dörfler * means we are done. 53580bca3d3SAxel Dörfler */ 53680bca3d3SAxel Dörfler if (!ntfs_is_empty_recordp((le32*)kaddr)) 53780bca3d3SAxel Dörfler logfile_is_empty = FALSE; 53880bca3d3SAxel Dörfler else if (!logfile_is_empty) 53980bca3d3SAxel Dörfler break; 54080bca3d3SAxel Dörfler /* 54180bca3d3SAxel Dörfler * A log record page means there cannot be a restart page after 54280bca3d3SAxel Dörfler * this so no need to continue searching. 54380bca3d3SAxel Dörfler */ 54480bca3d3SAxel Dörfler if (ntfs_is_rcrd_recordp((le32*)kaddr)) 54580bca3d3SAxel Dörfler break; 54680bca3d3SAxel Dörfler /* If not a (modified by chkdsk) restart page, continue. */ 54780bca3d3SAxel Dörfler if (!ntfs_is_rstr_recordp((le32*)kaddr) && 54880bca3d3SAxel Dörfler !ntfs_is_chkd_recordp((le32*)kaddr)) { 54980bca3d3SAxel Dörfler if (!pos) 55080bca3d3SAxel Dörfler pos = NTFS_BLOCK_SIZE >> 1; 55180bca3d3SAxel Dörfler continue; 55280bca3d3SAxel Dörfler } 55380bca3d3SAxel Dörfler /* 55480bca3d3SAxel Dörfler * Check the (modified by chkdsk) restart page for consistency 55580bca3d3SAxel Dörfler * and get a copy of the complete multi sector transfer 55680bca3d3SAxel Dörfler * deprotected restart page. 55780bca3d3SAxel Dörfler */ 55880bca3d3SAxel Dörfler err = ntfs_check_and_load_restart_page(log_na, 55980bca3d3SAxel Dörfler (RESTART_PAGE_HEADER*)kaddr, pos, 56080bca3d3SAxel Dörfler !rstr1_ph ? &rstr1_ph : &rstr2_ph, 56180bca3d3SAxel Dörfler !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); 56280bca3d3SAxel Dörfler if (!err) { 56380bca3d3SAxel Dörfler /* 56480bca3d3SAxel Dörfler * If we have now found the first (modified by chkdsk) 56580bca3d3SAxel Dörfler * restart page, continue looking for the second one. 56680bca3d3SAxel Dörfler */ 56780bca3d3SAxel Dörfler if (!pos) { 56880bca3d3SAxel Dörfler pos = NTFS_BLOCK_SIZE >> 1; 56980bca3d3SAxel Dörfler continue; 57080bca3d3SAxel Dörfler } 57180bca3d3SAxel Dörfler /* 57280bca3d3SAxel Dörfler * We have now found the second (modified by chkdsk) 57380bca3d3SAxel Dörfler * restart page, so we can stop looking. 57480bca3d3SAxel Dörfler */ 57580bca3d3SAxel Dörfler break; 57680bca3d3SAxel Dörfler } 57780bca3d3SAxel Dörfler /* 57880bca3d3SAxel Dörfler * Error output already done inside the function. Note, we do 57980bca3d3SAxel Dörfler * not abort if the restart page was invalid as we might still 58080bca3d3SAxel Dörfler * find a valid one further in the file. 58180bca3d3SAxel Dörfler */ 58280bca3d3SAxel Dörfler if (err != EINVAL) 58380bca3d3SAxel Dörfler goto err_out; 58480bca3d3SAxel Dörfler /* Continue looking. */ 58580bca3d3SAxel Dörfler if (!pos) 58680bca3d3SAxel Dörfler pos = NTFS_BLOCK_SIZE >> 1; 58780bca3d3SAxel Dörfler } 58880bca3d3SAxel Dörfler if (kaddr) { 58980bca3d3SAxel Dörfler free(kaddr); 59080bca3d3SAxel Dörfler kaddr = NULL; 59180bca3d3SAxel Dörfler } 59280bca3d3SAxel Dörfler if (logfile_is_empty) { 59380bca3d3SAxel Dörfler NVolSetLogFileEmpty(vol); 59480bca3d3SAxel Dörfler is_empty: 59580bca3d3SAxel Dörfler ntfs_log_trace("Done. ($LogFile is empty.)\n"); 59680bca3d3SAxel Dörfler return TRUE; 59780bca3d3SAxel Dörfler } 59880bca3d3SAxel Dörfler if (!rstr1_ph) { 59980bca3d3SAxel Dörfler if (rstr2_ph) 60080bca3d3SAxel Dörfler ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); 60180bca3d3SAxel Dörfler ntfs_log_error("Did not find any restart pages in " 60280bca3d3SAxel Dörfler "$LogFile and it was not empty.\n"); 60380bca3d3SAxel Dörfler return FALSE; 60480bca3d3SAxel Dörfler } 60580bca3d3SAxel Dörfler /* If both restart pages were found, use the more recent one. */ 60680bca3d3SAxel Dörfler if (rstr2_ph) { 60780bca3d3SAxel Dörfler /* 60880bca3d3SAxel Dörfler * If the second restart area is more recent, switch to it. 60980bca3d3SAxel Dörfler * Otherwise just throw it away. 61080bca3d3SAxel Dörfler */ 61180bca3d3SAxel Dörfler if (rstr2_lsn > rstr1_lsn) { 61280bca3d3SAxel Dörfler ntfs_log_debug("Using second restart page as it is more " 61380bca3d3SAxel Dörfler "recent.\n"); 61480bca3d3SAxel Dörfler free(rstr1_ph); 61580bca3d3SAxel Dörfler rstr1_ph = rstr2_ph; 61680bca3d3SAxel Dörfler /* rstr1_lsn = rstr2_lsn; */ 61780bca3d3SAxel Dörfler } else { 61880bca3d3SAxel Dörfler ntfs_log_debug("Using first restart page as it is more " 61980bca3d3SAxel Dörfler "recent.\n"); 62080bca3d3SAxel Dörfler free(rstr2_ph); 62180bca3d3SAxel Dörfler } 62280bca3d3SAxel Dörfler rstr2_ph = NULL; 62380bca3d3SAxel Dörfler } 62480bca3d3SAxel Dörfler /* All consistency checks passed. */ 62580bca3d3SAxel Dörfler if (rp) 62680bca3d3SAxel Dörfler *rp = rstr1_ph; 62780bca3d3SAxel Dörfler else 62880bca3d3SAxel Dörfler free(rstr1_ph); 62980bca3d3SAxel Dörfler ntfs_log_trace("Done.\n"); 63080bca3d3SAxel Dörfler return TRUE; 63180bca3d3SAxel Dörfler err_out: 63280bca3d3SAxel Dörfler free(kaddr); 63380bca3d3SAxel Dörfler free(rstr1_ph); 63480bca3d3SAxel Dörfler free(rstr2_ph); 63580bca3d3SAxel Dörfler return FALSE; 63680bca3d3SAxel Dörfler } 63780bca3d3SAxel Dörfler 63880bca3d3SAxel Dörfler /** 63980bca3d3SAxel Dörfler * ntfs_is_logfile_clean - check in the journal if the volume is clean 64080bca3d3SAxel Dörfler * @log_na: ntfs attribute of loaded journal $LogFile to check 64180bca3d3SAxel Dörfler * @rp: copy of the current restart page 64280bca3d3SAxel Dörfler * 64380bca3d3SAxel Dörfler * Analyze the $LogFile journal and return TRUE if it indicates the volume was 64480bca3d3SAxel Dörfler * shutdown cleanly and FALSE if not. 64580bca3d3SAxel Dörfler * 64680bca3d3SAxel Dörfler * At present we only look at the two restart pages and ignore the log record 64780bca3d3SAxel Dörfler * pages. This is a little bit crude in that there will be a very small number 64880bca3d3SAxel Dörfler * of cases where we think that a volume is dirty when in fact it is clean. 64980bca3d3SAxel Dörfler * This should only affect volumes that have not been shutdown cleanly but did 65080bca3d3SAxel Dörfler * not have any pending, non-check-pointed i/o, i.e. they were completely idle 65180bca3d3SAxel Dörfler * at least for the five seconds preceding the unclean shutdown. 65280bca3d3SAxel Dörfler * 65380bca3d3SAxel Dörfler * This function assumes that the $LogFile journal has already been consistency 65480bca3d3SAxel Dörfler * checked by a call to ntfs_check_logfile() and in particular if the $LogFile 65580bca3d3SAxel Dörfler * is empty this function requires that NVolLogFileEmpty() is true otherwise an 65680bca3d3SAxel Dörfler * empty volume will be reported as dirty. 65780bca3d3SAxel Dörfler */ 65880bca3d3SAxel Dörfler BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) 65980bca3d3SAxel Dörfler { 66080bca3d3SAxel Dörfler RESTART_AREA *ra; 66180bca3d3SAxel Dörfler 66280bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 66380bca3d3SAxel Dörfler /* An empty $LogFile must have been clean before it got emptied. */ 66480bca3d3SAxel Dörfler if (NVolLogFileEmpty(log_na->ni->vol)) { 6656b3592caSGerasim Troeglazov ntfs_log_trace("$LogFile is empty\n"); 66680bca3d3SAxel Dörfler return TRUE; 66780bca3d3SAxel Dörfler } 66880bca3d3SAxel Dörfler if (!rp) { 6696b3592caSGerasim Troeglazov ntfs_log_error("Restart page header is NULL\n"); 67080bca3d3SAxel Dörfler return FALSE; 67180bca3d3SAxel Dörfler } 67280bca3d3SAxel Dörfler if (!ntfs_is_rstr_record(rp->magic) && 67380bca3d3SAxel Dörfler !ntfs_is_chkd_record(rp->magic)) { 6746b3592caSGerasim Troeglazov ntfs_log_error("Restart page buffer is invalid\n"); 67580bca3d3SAxel Dörfler return FALSE; 67680bca3d3SAxel Dörfler } 67780bca3d3SAxel Dörfler 67880bca3d3SAxel Dörfler ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 67980bca3d3SAxel Dörfler /* 68080bca3d3SAxel Dörfler * If the $LogFile has active clients, i.e. it is open, and we do not 68180bca3d3SAxel Dörfler * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, 68280bca3d3SAxel Dörfler * we assume there was an unclean shutdown. 68380bca3d3SAxel Dörfler */ 68480bca3d3SAxel Dörfler if (ra->client_in_use_list != LOGFILE_NO_CLIENT && 68580bca3d3SAxel Dörfler !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { 68630bc84e9SGerasim Troeglazov ntfs_log_error("The disk contains an unclean file system (%d, " 68730bc84e9SGerasim Troeglazov "%d).\n", le16_to_cpu(ra->client_in_use_list), 6886b3592caSGerasim Troeglazov le16_to_cpu(ra->flags)); 68980bca3d3SAxel Dörfler return FALSE; 69080bca3d3SAxel Dörfler } 69180bca3d3SAxel Dörfler /* $LogFile indicates a clean shutdown. */ 6926b3592caSGerasim Troeglazov ntfs_log_trace("$LogFile indicates a clean shutdown\n"); 69380bca3d3SAxel Dörfler return TRUE; 69480bca3d3SAxel Dörfler } 69580bca3d3SAxel Dörfler 69680bca3d3SAxel Dörfler /** 69780bca3d3SAxel Dörfler * ntfs_empty_logfile - empty the contents of the $LogFile journal 69880bca3d3SAxel Dörfler * @na: ntfs attribute of journal $LogFile to empty 69980bca3d3SAxel Dörfler * 70080bca3d3SAxel Dörfler * Empty the contents of the $LogFile journal @na and return 0 on success and 70180bca3d3SAxel Dörfler * -1 on error. 70280bca3d3SAxel Dörfler * 70380bca3d3SAxel Dörfler * This function assumes that the $LogFile journal has already been consistency 70480bca3d3SAxel Dörfler * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() 70580bca3d3SAxel Dörfler * has been used to ensure that the $LogFile is clean. 70680bca3d3SAxel Dörfler */ 70780bca3d3SAxel Dörfler int ntfs_empty_logfile(ntfs_attr *na) 70880bca3d3SAxel Dörfler { 70962cee9f9SGerasim Troeglazov s64 pos, count; 71080bca3d3SAxel Dörfler char buf[NTFS_BUF_SIZE]; 71180bca3d3SAxel Dörfler 71280bca3d3SAxel Dörfler ntfs_log_trace("Entering.\n"); 71362cee9f9SGerasim Troeglazov 71480bca3d3SAxel Dörfler if (NVolLogFileEmpty(na->ni->vol)) 71580bca3d3SAxel Dörfler return 0; 71680bca3d3SAxel Dörfler 71780bca3d3SAxel Dörfler if (!NAttrNonResident(na)) { 71880bca3d3SAxel Dörfler errno = EIO; 71962cee9f9SGerasim Troeglazov ntfs_log_perror("Resident $LogFile $DATA attribute"); 72080bca3d3SAxel Dörfler return -1; 72180bca3d3SAxel Dörfler } 72280bca3d3SAxel Dörfler 72380bca3d3SAxel Dörfler memset(buf, -1, NTFS_BUF_SIZE); 72480bca3d3SAxel Dörfler 72580bca3d3SAxel Dörfler pos = 0; 72662cee9f9SGerasim Troeglazov while ((count = na->data_size - pos) > 0) { 72762cee9f9SGerasim Troeglazov 72880bca3d3SAxel Dörfler if (count > NTFS_BUF_SIZE) 72980bca3d3SAxel Dörfler count = NTFS_BUF_SIZE; 73080bca3d3SAxel Dörfler 73162cee9f9SGerasim Troeglazov count = ntfs_attr_pwrite(na, pos, count, buf); 73262cee9f9SGerasim Troeglazov if (count <= 0) { 73362cee9f9SGerasim Troeglazov ntfs_log_perror("Failed to reset $LogFile"); 73480bca3d3SAxel Dörfler if (count != -1) 73580bca3d3SAxel Dörfler errno = EIO; 73680bca3d3SAxel Dörfler return -1; 73780bca3d3SAxel Dörfler } 73880bca3d3SAxel Dörfler pos += count; 73980bca3d3SAxel Dörfler } 74080bca3d3SAxel Dörfler 74180bca3d3SAxel Dörfler NVolSetLogFileEmpty(na->ni->vol); 74262cee9f9SGerasim Troeglazov 74380bca3d3SAxel Dörfler return 0; 74480bca3d3SAxel Dörfler } 745