1987da915Sopenharmony_ci/** 2987da915Sopenharmony_ci * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project. 3987da915Sopenharmony_ci * 4987da915Sopenharmony_ci * Copyright (c) 2002-2005 Anton Altaparmakov 5987da915Sopenharmony_ci * Copyright (c) 2005 Yura Pakhuchiy 6987da915Sopenharmony_ci * Copyright (c) 2005-2009 Szabolcs Szakacsits 7987da915Sopenharmony_ci * 8987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or 9987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published 10987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or 11987da915Sopenharmony_ci * (at your option) any later version. 12987da915Sopenharmony_ci * 13987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be 14987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 15987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16987da915Sopenharmony_ci * GNU General Public License for more details. 17987da915Sopenharmony_ci * 18987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License 19987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G 20987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software 21987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22987da915Sopenharmony_ci */ 23987da915Sopenharmony_ci 24987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H 25987da915Sopenharmony_ci#include "config.h" 26987da915Sopenharmony_ci#endif 27987da915Sopenharmony_ci 28987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H 29987da915Sopenharmony_ci#include <stdlib.h> 30987da915Sopenharmony_ci#endif 31987da915Sopenharmony_ci#ifdef HAVE_STRING_H 32987da915Sopenharmony_ci#include <string.h> 33987da915Sopenharmony_ci#endif 34987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H 35987da915Sopenharmony_ci#include <errno.h> 36987da915Sopenharmony_ci#endif 37987da915Sopenharmony_ci 38987da915Sopenharmony_ci#include "attrib.h" 39987da915Sopenharmony_ci#include "debug.h" 40987da915Sopenharmony_ci#include "logfile.h" 41987da915Sopenharmony_ci#include "volume.h" 42987da915Sopenharmony_ci#include "mst.h" 43987da915Sopenharmony_ci#include "logging.h" 44987da915Sopenharmony_ci#include "misc.h" 45987da915Sopenharmony_ci 46987da915Sopenharmony_ci/** 47987da915Sopenharmony_ci * ntfs_check_restart_page_header - check the page header for consistency 48987da915Sopenharmony_ci * @rp: restart page header to check 49987da915Sopenharmony_ci * @pos: position in logfile at which the restart page header resides 50987da915Sopenharmony_ci * 51987da915Sopenharmony_ci * Check the restart page header @rp for consistency and return TRUE if it is 52987da915Sopenharmony_ci * consistent and FALSE otherwise. 53987da915Sopenharmony_ci * 54987da915Sopenharmony_ci * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 55987da915Sopenharmony_ci * require the full restart page. 56987da915Sopenharmony_ci */ 57987da915Sopenharmony_cistatic BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos) 58987da915Sopenharmony_ci{ 59987da915Sopenharmony_ci u32 logfile_system_page_size, logfile_log_page_size; 60987da915Sopenharmony_ci u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; 61987da915Sopenharmony_ci BOOL have_usa = TRUE; 62987da915Sopenharmony_ci 63987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 64987da915Sopenharmony_ci /* 65987da915Sopenharmony_ci * If the system or log page sizes are smaller than the ntfs block size 66987da915Sopenharmony_ci * or either is not a power of 2 we cannot handle this log file. 67987da915Sopenharmony_ci */ 68987da915Sopenharmony_ci logfile_system_page_size = le32_to_cpu(rp->system_page_size); 69987da915Sopenharmony_ci logfile_log_page_size = le32_to_cpu(rp->log_page_size); 70987da915Sopenharmony_ci if (logfile_system_page_size < NTFS_BLOCK_SIZE || 71987da915Sopenharmony_ci logfile_log_page_size < NTFS_BLOCK_SIZE || 72987da915Sopenharmony_ci logfile_system_page_size & 73987da915Sopenharmony_ci (logfile_system_page_size - 1) || 74987da915Sopenharmony_ci logfile_log_page_size & (logfile_log_page_size - 1)) { 75987da915Sopenharmony_ci ntfs_log_error("$LogFile uses unsupported page size.\n"); 76987da915Sopenharmony_ci return FALSE; 77987da915Sopenharmony_ci } 78987da915Sopenharmony_ci /* 79987da915Sopenharmony_ci * We must be either at !pos (1st restart page) or at pos = system page 80987da915Sopenharmony_ci * size (2nd restart page). 81987da915Sopenharmony_ci */ 82987da915Sopenharmony_ci if (pos && pos != logfile_system_page_size) { 83987da915Sopenharmony_ci ntfs_log_error("Found restart area in incorrect " 84987da915Sopenharmony_ci "position in $LogFile.\n"); 85987da915Sopenharmony_ci return FALSE; 86987da915Sopenharmony_ci } 87987da915Sopenharmony_ci /* 88987da915Sopenharmony_ci * We only know how to handle version 1.1 and 2.0, though 89987da915Sopenharmony_ci * version 2.0 is probably related to cached metadata in 90987da915Sopenharmony_ci * Windows 8, and we will refuse to mount. 91987da915Sopenharmony_ci * Nevertheless, do all the relevant checks before rejecting. 92987da915Sopenharmony_ci */ 93987da915Sopenharmony_ci if (((rp->major_ver != const_cpu_to_sle16(1)) 94987da915Sopenharmony_ci || (rp->minor_ver != const_cpu_to_sle16(1))) 95987da915Sopenharmony_ci && ((rp->major_ver != const_cpu_to_sle16(2)) 96987da915Sopenharmony_ci || (rp->minor_ver != const_cpu_to_sle16(0)))) { 97987da915Sopenharmony_ci ntfs_log_error("$LogFile version %i.%i is not " 98987da915Sopenharmony_ci "supported.\n (This driver supports version " 99987da915Sopenharmony_ci "1.1 and 2.0 only.)\n", 100987da915Sopenharmony_ci (int)sle16_to_cpu(rp->major_ver), 101987da915Sopenharmony_ci (int)sle16_to_cpu(rp->minor_ver)); 102987da915Sopenharmony_ci return FALSE; 103987da915Sopenharmony_ci } 104987da915Sopenharmony_ci /* 105987da915Sopenharmony_ci * If chkdsk has been run the restart page may not be protected by an 106987da915Sopenharmony_ci * update sequence array. 107987da915Sopenharmony_ci */ 108987da915Sopenharmony_ci if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { 109987da915Sopenharmony_ci have_usa = FALSE; 110987da915Sopenharmony_ci goto skip_usa_checks; 111987da915Sopenharmony_ci } 112987da915Sopenharmony_ci /* Verify the size of the update sequence array. */ 113987da915Sopenharmony_ci usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS); 114987da915Sopenharmony_ci if (usa_count != le16_to_cpu(rp->usa_count)) { 115987da915Sopenharmony_ci ntfs_log_error("$LogFile restart page specifies " 116987da915Sopenharmony_ci "inconsistent update sequence array count.\n"); 117987da915Sopenharmony_ci return FALSE; 118987da915Sopenharmony_ci } 119987da915Sopenharmony_ci /* Verify the position of the update sequence array. */ 120987da915Sopenharmony_ci usa_ofs = le16_to_cpu(rp->usa_ofs); 121987da915Sopenharmony_ci usa_end = usa_ofs + usa_count * sizeof(u16); 122987da915Sopenharmony_ci if (usa_ofs < offsetof(RESTART_PAGE_HEADER, usn) || 123987da915Sopenharmony_ci usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { 124987da915Sopenharmony_ci ntfs_log_error("$LogFile restart page specifies " 125987da915Sopenharmony_ci "inconsistent update sequence array offset.\n"); 126987da915Sopenharmony_ci return FALSE; 127987da915Sopenharmony_ci } 128987da915Sopenharmony_ciskip_usa_checks: 129987da915Sopenharmony_ci /* 130987da915Sopenharmony_ci * Verify the position of the restart area. It must be: 131987da915Sopenharmony_ci * - aligned to 8-byte boundary, 132987da915Sopenharmony_ci * - after the update sequence array, and 133987da915Sopenharmony_ci * - within the system page size. 134987da915Sopenharmony_ci */ 135987da915Sopenharmony_ci ra_ofs = le16_to_cpu(rp->restart_area_offset); 136987da915Sopenharmony_ci if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : 137987da915Sopenharmony_ci ra_ofs < offsetof(RESTART_PAGE_HEADER, usn)) || 138987da915Sopenharmony_ci ra_ofs > logfile_system_page_size) { 139987da915Sopenharmony_ci ntfs_log_error("$LogFile restart page specifies " 140987da915Sopenharmony_ci "inconsistent restart area offset.\n"); 141987da915Sopenharmony_ci return FALSE; 142987da915Sopenharmony_ci } 143987da915Sopenharmony_ci /* 144987da915Sopenharmony_ci * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn 145987da915Sopenharmony_ci * set. 146987da915Sopenharmony_ci */ 147987da915Sopenharmony_ci if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { 148987da915Sopenharmony_ci ntfs_log_error("$LogFile restart page is not modified " 149987da915Sopenharmony_ci "by chkdsk but a chkdsk LSN is specified.\n"); 150987da915Sopenharmony_ci return FALSE; 151987da915Sopenharmony_ci } 152987da915Sopenharmony_ci ntfs_log_trace("Done.\n"); 153987da915Sopenharmony_ci return TRUE; 154987da915Sopenharmony_ci} 155987da915Sopenharmony_ci 156987da915Sopenharmony_ci/** 157987da915Sopenharmony_ci * ntfs_check_restart_area - check the restart area for consistency 158987da915Sopenharmony_ci * @rp: restart page whose restart area to check 159987da915Sopenharmony_ci * 160987da915Sopenharmony_ci * Check the restart area of the restart page @rp for consistency and return 161987da915Sopenharmony_ci * TRUE if it is consistent and FALSE otherwise. 162987da915Sopenharmony_ci * 163987da915Sopenharmony_ci * This function assumes that the restart page header has already been 164987da915Sopenharmony_ci * consistency checked. 165987da915Sopenharmony_ci * 166987da915Sopenharmony_ci * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 167987da915Sopenharmony_ci * require the full restart page. 168987da915Sopenharmony_ci */ 169987da915Sopenharmony_cistatic BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) 170987da915Sopenharmony_ci{ 171987da915Sopenharmony_ci u64 file_size; 172987da915Sopenharmony_ci RESTART_AREA *ra; 173987da915Sopenharmony_ci u16 ra_ofs, ra_len, ca_ofs; 174987da915Sopenharmony_ci u8 fs_bits; 175987da915Sopenharmony_ci 176987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 177987da915Sopenharmony_ci ra_ofs = le16_to_cpu(rp->restart_area_offset); 178987da915Sopenharmony_ci ra = (RESTART_AREA*)((u8*)rp + ra_ofs); 179987da915Sopenharmony_ci /* 180987da915Sopenharmony_ci * Everything before ra->file_size must be before the first word 181987da915Sopenharmony_ci * protected by an update sequence number. This ensures that it is 182987da915Sopenharmony_ci * safe to access ra->client_array_offset. 183987da915Sopenharmony_ci */ 184987da915Sopenharmony_ci if (ra_ofs + offsetof(RESTART_AREA, file_size) > 185987da915Sopenharmony_ci NTFS_BLOCK_SIZE - sizeof(u16)) { 186987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area specifies " 187987da915Sopenharmony_ci "inconsistent file offset.\n"); 188987da915Sopenharmony_ci return FALSE; 189987da915Sopenharmony_ci } 190987da915Sopenharmony_ci /* 191987da915Sopenharmony_ci * Now that we can access ra->client_array_offset, make sure everything 192987da915Sopenharmony_ci * up to the log client array is before the first word protected by an 193987da915Sopenharmony_ci * update sequence number. This ensures we can access all of the 194987da915Sopenharmony_ci * restart area elements safely. Also, the client array offset must be 195987da915Sopenharmony_ci * aligned to an 8-byte boundary. 196987da915Sopenharmony_ci */ 197987da915Sopenharmony_ci ca_ofs = le16_to_cpu(ra->client_array_offset); 198987da915Sopenharmony_ci if (((ca_ofs + 7) & ~7) != ca_ofs || 199987da915Sopenharmony_ci ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE - 200987da915Sopenharmony_ci sizeof(u16))) { 201987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area specifies " 202987da915Sopenharmony_ci "inconsistent client array offset.\n"); 203987da915Sopenharmony_ci return FALSE; 204987da915Sopenharmony_ci } 205987da915Sopenharmony_ci /* 206987da915Sopenharmony_ci * The restart area must end within the system page size both when 207987da915Sopenharmony_ci * calculated manually and as specified by ra->restart_area_length. 208987da915Sopenharmony_ci * Also, the calculated length must not exceed the specified length. 209987da915Sopenharmony_ci */ 210987da915Sopenharmony_ci ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * 211987da915Sopenharmony_ci sizeof(LOG_CLIENT_RECORD); 212987da915Sopenharmony_ci if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) || 213987da915Sopenharmony_ci (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) > 214987da915Sopenharmony_ci le32_to_cpu(rp->system_page_size) || 215987da915Sopenharmony_ci ra_len > le16_to_cpu(ra->restart_area_length)) { 216987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area is out of bounds " 217987da915Sopenharmony_ci "of the system page size specified by the " 218987da915Sopenharmony_ci "restart page header and/or the specified " 219987da915Sopenharmony_ci "restart area length is inconsistent.\n"); 220987da915Sopenharmony_ci return FALSE; 221987da915Sopenharmony_ci } 222987da915Sopenharmony_ci /* 223987da915Sopenharmony_ci * The ra->client_free_list and ra->client_in_use_list must be either 224987da915Sopenharmony_ci * LOGFILE_NO_CLIENT or less than ra->log_clients or they are 225987da915Sopenharmony_ci * overflowing the client array. 226987da915Sopenharmony_ci */ 227987da915Sopenharmony_ci if ((ra->client_free_list != LOGFILE_NO_CLIENT && 228987da915Sopenharmony_ci le16_to_cpu(ra->client_free_list) >= 229987da915Sopenharmony_ci le16_to_cpu(ra->log_clients)) || 230987da915Sopenharmony_ci (ra->client_in_use_list != LOGFILE_NO_CLIENT && 231987da915Sopenharmony_ci le16_to_cpu(ra->client_in_use_list) >= 232987da915Sopenharmony_ci le16_to_cpu(ra->log_clients))) { 233987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area specifies " 234987da915Sopenharmony_ci "overflowing client free and/or in use lists.\n"); 235987da915Sopenharmony_ci return FALSE; 236987da915Sopenharmony_ci } 237987da915Sopenharmony_ci /* 238987da915Sopenharmony_ci * Check ra->seq_number_bits against ra->file_size for consistency. 239987da915Sopenharmony_ci * We cannot just use ffs() because the file size is not a power of 2. 240987da915Sopenharmony_ci */ 241987da915Sopenharmony_ci file_size = (u64)sle64_to_cpu(ra->file_size); 242987da915Sopenharmony_ci fs_bits = 0; 243987da915Sopenharmony_ci while (file_size) { 244987da915Sopenharmony_ci file_size >>= 1; 245987da915Sopenharmony_ci fs_bits++; 246987da915Sopenharmony_ci } 247987da915Sopenharmony_ci if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) { 248987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area specifies " 249987da915Sopenharmony_ci "inconsistent sequence number bits.\n"); 250987da915Sopenharmony_ci return FALSE; 251987da915Sopenharmony_ci } 252987da915Sopenharmony_ci /* The log record header length must be a multiple of 8. */ 253987da915Sopenharmony_ci if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != 254987da915Sopenharmony_ci le16_to_cpu(ra->log_record_header_length)) { 255987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area specifies " 256987da915Sopenharmony_ci "inconsistent log record header length.\n"); 257987da915Sopenharmony_ci return FALSE; 258987da915Sopenharmony_ci } 259987da915Sopenharmony_ci /* Ditto for the log page data offset. */ 260987da915Sopenharmony_ci if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != 261987da915Sopenharmony_ci le16_to_cpu(ra->log_page_data_offset)) { 262987da915Sopenharmony_ci ntfs_log_error("$LogFile restart area specifies " 263987da915Sopenharmony_ci "inconsistent log page data offset.\n"); 264987da915Sopenharmony_ci return FALSE; 265987da915Sopenharmony_ci } 266987da915Sopenharmony_ci ntfs_log_trace("Done.\n"); 267987da915Sopenharmony_ci return TRUE; 268987da915Sopenharmony_ci} 269987da915Sopenharmony_ci 270987da915Sopenharmony_ci/** 271987da915Sopenharmony_ci * ntfs_check_log_client_array - check the log client array for consistency 272987da915Sopenharmony_ci * @rp: restart page whose log client array to check 273987da915Sopenharmony_ci * 274987da915Sopenharmony_ci * Check the log client array of the restart page @rp for consistency and 275987da915Sopenharmony_ci * return TRUE if it is consistent and FALSE otherwise. 276987da915Sopenharmony_ci * 277987da915Sopenharmony_ci * This function assumes that the restart page header and the restart area have 278987da915Sopenharmony_ci * already been consistency checked. 279987da915Sopenharmony_ci * 280987da915Sopenharmony_ci * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this 281987da915Sopenharmony_ci * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full 282987da915Sopenharmony_ci * restart page and the page must be multi sector transfer deprotected. 283987da915Sopenharmony_ci */ 284987da915Sopenharmony_cistatic BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp) 285987da915Sopenharmony_ci{ 286987da915Sopenharmony_ci RESTART_AREA *ra; 287987da915Sopenharmony_ci LOG_CLIENT_RECORD *ca, *cr; 288987da915Sopenharmony_ci u16 nr_clients, idx; 289987da915Sopenharmony_ci BOOL in_free_list, idx_is_first; 290987da915Sopenharmony_ci u32 offset_clients; 291987da915Sopenharmony_ci 292987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 293987da915Sopenharmony_ci /* The restart area must be fully within page */ 294987da915Sopenharmony_ci if ((le16_to_cpu(rp->restart_area_offset) + sizeof(RESTART_AREA)) 295987da915Sopenharmony_ci > le32_to_cpu(rp->system_page_size)) 296987da915Sopenharmony_ci goto err_out; 297987da915Sopenharmony_ci ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 298987da915Sopenharmony_ci offset_clients = le16_to_cpu(rp->restart_area_offset) 299987da915Sopenharmony_ci + le16_to_cpu(ra->client_array_offset); 300987da915Sopenharmony_ci /* The clients' records must begin within page */ 301987da915Sopenharmony_ci if (offset_clients >= le32_to_cpu(rp->system_page_size)) 302987da915Sopenharmony_ci goto err_out; 303987da915Sopenharmony_ci ca = (LOG_CLIENT_RECORD*)((u8*)ra + 304987da915Sopenharmony_ci le16_to_cpu(ra->client_array_offset)); 305987da915Sopenharmony_ci /* 306987da915Sopenharmony_ci * Check the ra->client_free_list first and then check the 307987da915Sopenharmony_ci * ra->client_in_use_list. Check each of the log client records in 308987da915Sopenharmony_ci * each of the lists and check that the array does not overflow the 309987da915Sopenharmony_ci * ra->log_clients value. Also keep track of the number of records 310987da915Sopenharmony_ci * visited as there cannot be more than ra->log_clients records and 311987da915Sopenharmony_ci * that way we detect eventual loops in within a list. 312987da915Sopenharmony_ci */ 313987da915Sopenharmony_ci nr_clients = le16_to_cpu(ra->log_clients); 314987da915Sopenharmony_ci idx = le16_to_cpu(ra->client_free_list); 315987da915Sopenharmony_ci in_free_list = TRUE; 316987da915Sopenharmony_cicheck_list: 317987da915Sopenharmony_ci for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, 318987da915Sopenharmony_ci idx = le16_to_cpu(cr->next_client)) { 319987da915Sopenharmony_ci if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) 320987da915Sopenharmony_ci goto err_out; 321987da915Sopenharmony_ci /* The client record must be fully within page */ 322987da915Sopenharmony_ci if ((offset_clients + (idx + 1)*sizeof(LOG_CLIENT_RECORD)) 323987da915Sopenharmony_ci > le32_to_cpu(rp->system_page_size)) 324987da915Sopenharmony_ci goto err_out; 325987da915Sopenharmony_ci /* Set @cr to the current log client record. */ 326987da915Sopenharmony_ci cr = ca + idx; 327987da915Sopenharmony_ci /* The first log client record must not have a prev_client. */ 328987da915Sopenharmony_ci if (idx_is_first) { 329987da915Sopenharmony_ci if (cr->prev_client != LOGFILE_NO_CLIENT) 330987da915Sopenharmony_ci goto err_out; 331987da915Sopenharmony_ci idx_is_first = FALSE; 332987da915Sopenharmony_ci } 333987da915Sopenharmony_ci } 334987da915Sopenharmony_ci /* Switch to and check the in use list if we just did the free list. */ 335987da915Sopenharmony_ci if (in_free_list) { 336987da915Sopenharmony_ci in_free_list = FALSE; 337987da915Sopenharmony_ci idx = le16_to_cpu(ra->client_in_use_list); 338987da915Sopenharmony_ci goto check_list; 339987da915Sopenharmony_ci } 340987da915Sopenharmony_ci ntfs_log_trace("Done.\n"); 341987da915Sopenharmony_ci return TRUE; 342987da915Sopenharmony_cierr_out: 343987da915Sopenharmony_ci ntfs_log_error("$LogFile log client array is corrupt.\n"); 344987da915Sopenharmony_ci return FALSE; 345987da915Sopenharmony_ci} 346987da915Sopenharmony_ci 347987da915Sopenharmony_ci/** 348987da915Sopenharmony_ci * ntfs_check_and_load_restart_page - check the restart page for consistency 349987da915Sopenharmony_ci * @log_na: opened ntfs attribute for journal $LogFile 350987da915Sopenharmony_ci * @rp: restart page to check 351987da915Sopenharmony_ci * @pos: position in @log_na at which the restart page resides 352987da915Sopenharmony_ci * @wrp: [OUT] copy of the multi sector transfer deprotected restart page 353987da915Sopenharmony_ci * @lsn: [OUT] set to the current logfile lsn on success 354987da915Sopenharmony_ci * 355987da915Sopenharmony_ci * Check the restart page @rp for consistency and return 0 if it is consistent 356987da915Sopenharmony_ci * and errno otherwise. The restart page may have been modified by chkdsk in 357987da915Sopenharmony_ci * which case its magic is CHKD instead of RSTR. 358987da915Sopenharmony_ci * 359987da915Sopenharmony_ci * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 360987da915Sopenharmony_ci * require the full restart page. 361987da915Sopenharmony_ci * 362987da915Sopenharmony_ci * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a 363987da915Sopenharmony_ci * copy of the complete multi sector transfer deprotected page. On failure, 364987da915Sopenharmony_ci * *@wrp is undefined. 365987da915Sopenharmony_ci * 366987da915Sopenharmony_ci * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current 367987da915Sopenharmony_ci * logfile lsn according to this restart page. On failure, *@lsn is undefined. 368987da915Sopenharmony_ci * 369987da915Sopenharmony_ci * The following error codes are defined: 370987da915Sopenharmony_ci * EINVAL - The restart page is inconsistent. 371987da915Sopenharmony_ci * ENOMEM - Not enough memory to load the restart page. 372987da915Sopenharmony_ci * EIO - Failed to reading from $LogFile. 373987da915Sopenharmony_ci */ 374987da915Sopenharmony_cistatic int ntfs_check_and_load_restart_page(ntfs_attr *log_na, 375987da915Sopenharmony_ci RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp, 376987da915Sopenharmony_ci LSN *lsn) 377987da915Sopenharmony_ci{ 378987da915Sopenharmony_ci RESTART_AREA *ra; 379987da915Sopenharmony_ci RESTART_PAGE_HEADER *trp; 380987da915Sopenharmony_ci int err; 381987da915Sopenharmony_ci 382987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 383987da915Sopenharmony_ci /* Check the restart page header for consistency. */ 384987da915Sopenharmony_ci if (!ntfs_check_restart_page_header(rp, pos)) { 385987da915Sopenharmony_ci /* Error output already done inside the function. */ 386987da915Sopenharmony_ci return EINVAL; 387987da915Sopenharmony_ci } 388987da915Sopenharmony_ci /* Check the restart area for consistency. */ 389987da915Sopenharmony_ci if (!ntfs_check_restart_area(rp)) { 390987da915Sopenharmony_ci /* Error output already done inside the function. */ 391987da915Sopenharmony_ci return EINVAL; 392987da915Sopenharmony_ci } 393987da915Sopenharmony_ci ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 394987da915Sopenharmony_ci /* 395987da915Sopenharmony_ci * Allocate a buffer to store the whole restart page so we can multi 396987da915Sopenharmony_ci * sector transfer deprotect it. 397987da915Sopenharmony_ci * For safety, make sure this is consistent with the usa_count 398987da915Sopenharmony_ci * and shorter than the full log size 399987da915Sopenharmony_ci */ 400987da915Sopenharmony_ci if ((le32_to_cpu(rp->system_page_size) 401987da915Sopenharmony_ci > (u32)(le16_to_cpu(rp->usa_count) - 1)*NTFS_BLOCK_SIZE) 402987da915Sopenharmony_ci || (le32_to_cpu(rp->system_page_size) 403987da915Sopenharmony_ci > le64_to_cpu(log_na->data_size))) 404987da915Sopenharmony_ci return (EINVAL); 405987da915Sopenharmony_ci trp = ntfs_malloc(le32_to_cpu(rp->system_page_size)); 406987da915Sopenharmony_ci if (!trp) 407987da915Sopenharmony_ci return errno; 408987da915Sopenharmony_ci /* 409987da915Sopenharmony_ci * Read the whole of the restart page into the buffer. If it fits 410987da915Sopenharmony_ci * completely inside @rp, just copy it from there. Otherwise read it 411987da915Sopenharmony_ci * from disk. 412987da915Sopenharmony_ci */ 413987da915Sopenharmony_ci if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE) 414987da915Sopenharmony_ci memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); 415987da915Sopenharmony_ci else if (ntfs_attr_pread(log_na, pos, 416987da915Sopenharmony_ci le32_to_cpu(rp->system_page_size), trp) != 417987da915Sopenharmony_ci le32_to_cpu(rp->system_page_size)) { 418987da915Sopenharmony_ci err = errno; 419987da915Sopenharmony_ci ntfs_log_error("Failed to read whole restart page into the " 420987da915Sopenharmony_ci "buffer.\n"); 421987da915Sopenharmony_ci if (err != ENOMEM) 422987da915Sopenharmony_ci err = EIO; 423987da915Sopenharmony_ci goto err_out; 424987da915Sopenharmony_ci } 425987da915Sopenharmony_ci /* 426987da915Sopenharmony_ci * Perform the multi sector transfer deprotection on the buffer if the 427987da915Sopenharmony_ci * restart page is protected. 428987da915Sopenharmony_ci */ 429987da915Sopenharmony_ci if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) 430987da915Sopenharmony_ci && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, 431987da915Sopenharmony_ci le32_to_cpu(rp->system_page_size))) { 432987da915Sopenharmony_ci /* 433987da915Sopenharmony_ci * A multi sector tranfer error was detected. We only need to 434987da915Sopenharmony_ci * abort if the restart page contents exceed the multi sector 435987da915Sopenharmony_ci * transfer fixup of the first sector. 436987da915Sopenharmony_ci */ 437987da915Sopenharmony_ci if (le16_to_cpu(rp->restart_area_offset) + 438987da915Sopenharmony_ci le16_to_cpu(ra->restart_area_length) > 439987da915Sopenharmony_ci NTFS_BLOCK_SIZE - (int)sizeof(u16)) { 440987da915Sopenharmony_ci ntfs_log_error("Multi sector transfer error " 441987da915Sopenharmony_ci "detected in $LogFile restart page.\n"); 442987da915Sopenharmony_ci err = EINVAL; 443987da915Sopenharmony_ci goto err_out; 444987da915Sopenharmony_ci } 445987da915Sopenharmony_ci } 446987da915Sopenharmony_ci /* 447987da915Sopenharmony_ci * If the restart page is modified by chkdsk or there are no active 448987da915Sopenharmony_ci * logfile clients, the logfile is consistent. Otherwise, need to 449987da915Sopenharmony_ci * check the log client records for consistency, too. 450987da915Sopenharmony_ci */ 451987da915Sopenharmony_ci err = 0; 452987da915Sopenharmony_ci if (ntfs_is_rstr_record(rp->magic) && 453987da915Sopenharmony_ci ra->client_in_use_list != LOGFILE_NO_CLIENT) { 454987da915Sopenharmony_ci if (!ntfs_check_log_client_array(trp)) { 455987da915Sopenharmony_ci err = EINVAL; 456987da915Sopenharmony_ci goto err_out; 457987da915Sopenharmony_ci } 458987da915Sopenharmony_ci } 459987da915Sopenharmony_ci if (lsn) { 460987da915Sopenharmony_ci if (ntfs_is_rstr_record(rp->magic)) 461987da915Sopenharmony_ci *lsn = sle64_to_cpu(ra->current_lsn); 462987da915Sopenharmony_ci else /* if (ntfs_is_chkd_record(rp->magic)) */ 463987da915Sopenharmony_ci *lsn = sle64_to_cpu(rp->chkdsk_lsn); 464987da915Sopenharmony_ci } 465987da915Sopenharmony_ci ntfs_log_trace("Done.\n"); 466987da915Sopenharmony_ci if (wrp) 467987da915Sopenharmony_ci *wrp = trp; 468987da915Sopenharmony_ci else { 469987da915Sopenharmony_cierr_out: 470987da915Sopenharmony_ci free(trp); 471987da915Sopenharmony_ci } 472987da915Sopenharmony_ci return err; 473987da915Sopenharmony_ci} 474987da915Sopenharmony_ci 475987da915Sopenharmony_ci/** 476987da915Sopenharmony_ci * ntfs_check_logfile - check in the journal if the volume is consistent 477987da915Sopenharmony_ci * @log_na: ntfs attribute of loaded journal $LogFile to check 478987da915Sopenharmony_ci * @rp: [OUT] on success this is a copy of the current restart page 479987da915Sopenharmony_ci * 480987da915Sopenharmony_ci * Check the $LogFile journal for consistency and return TRUE if it is 481987da915Sopenharmony_ci * consistent and FALSE if not. On success, the current restart page is 482987da915Sopenharmony_ci * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. 483987da915Sopenharmony_ci * 484987da915Sopenharmony_ci * At present we only check the two restart pages and ignore the log record 485987da915Sopenharmony_ci * pages. 486987da915Sopenharmony_ci * 487987da915Sopenharmony_ci * Note that the MstProtected flag is not set on the $LogFile inode and hence 488987da915Sopenharmony_ci * when reading pages they are not deprotected. This is because we do not know 489987da915Sopenharmony_ci * if the $LogFile was created on a system with a different page size to ours 490987da915Sopenharmony_ci * yet and mst deprotection would fail if our page size is smaller. 491987da915Sopenharmony_ci */ 492987da915Sopenharmony_ciBOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp) 493987da915Sopenharmony_ci{ 494987da915Sopenharmony_ci s64 size, pos; 495987da915Sopenharmony_ci LSN rstr1_lsn, rstr2_lsn; 496987da915Sopenharmony_ci ntfs_volume *vol = log_na->ni->vol; 497987da915Sopenharmony_ci u8 *kaddr = NULL; 498987da915Sopenharmony_ci RESTART_PAGE_HEADER *rstr1_ph = NULL; 499987da915Sopenharmony_ci RESTART_PAGE_HEADER *rstr2_ph = NULL; 500987da915Sopenharmony_ci int log_page_size, err; 501987da915Sopenharmony_ci BOOL logfile_is_empty = TRUE; 502987da915Sopenharmony_ci u8 log_page_bits; 503987da915Sopenharmony_ci 504987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 505987da915Sopenharmony_ci /* An empty $LogFile must have been clean before it got emptied. */ 506987da915Sopenharmony_ci if (NVolLogFileEmpty(vol)) 507987da915Sopenharmony_ci goto is_empty; 508987da915Sopenharmony_ci size = log_na->data_size; 509987da915Sopenharmony_ci /* Make sure the file doesn't exceed the maximum allowed size. */ 510987da915Sopenharmony_ci if (size > (s64)MaxLogFileSize) 511987da915Sopenharmony_ci size = MaxLogFileSize; 512987da915Sopenharmony_ci log_page_size = DefaultLogPageSize; 513987da915Sopenharmony_ci /* 514987da915Sopenharmony_ci * Use generic_ffs() instead of ffs() to enable the compiler to 515987da915Sopenharmony_ci * optimize log_page_size and log_page_bits into constants. 516987da915Sopenharmony_ci */ 517987da915Sopenharmony_ci log_page_bits = ffs(log_page_size) - 1; 518987da915Sopenharmony_ci size &= ~(log_page_size - 1); 519987da915Sopenharmony_ci 520987da915Sopenharmony_ci /* 521987da915Sopenharmony_ci * Ensure the log file is big enough to store at least the two restart 522987da915Sopenharmony_ci * pages and the minimum number of log record pages. 523987da915Sopenharmony_ci */ 524987da915Sopenharmony_ci if (size < log_page_size * 2 || (size - log_page_size * 2) >> 525987da915Sopenharmony_ci log_page_bits < MinLogRecordPages) { 526987da915Sopenharmony_ci ntfs_log_error("$LogFile is too small.\n"); 527987da915Sopenharmony_ci return FALSE; 528987da915Sopenharmony_ci } 529987da915Sopenharmony_ci /* Allocate memory for restart page. */ 530987da915Sopenharmony_ci kaddr = ntfs_malloc(NTFS_BLOCK_SIZE); 531987da915Sopenharmony_ci if (!kaddr) 532987da915Sopenharmony_ci return FALSE; 533987da915Sopenharmony_ci /* 534987da915Sopenharmony_ci * Read through the file looking for a restart page. Since the restart 535987da915Sopenharmony_ci * page header is at the beginning of a page we only need to search at 536987da915Sopenharmony_ci * what could be the beginning of a page (for each page size) rather 537987da915Sopenharmony_ci * than scanning the whole file byte by byte. If all potential places 538987da915Sopenharmony_ci * contain empty and uninitialized records, the log file can be assumed 539987da915Sopenharmony_ci * to be empty. 540987da915Sopenharmony_ci */ 541987da915Sopenharmony_ci for (pos = 0; pos < size; pos <<= 1) { 542987da915Sopenharmony_ci /* 543987da915Sopenharmony_ci * Read first NTFS_BLOCK_SIZE bytes of potential restart page. 544987da915Sopenharmony_ci */ 545987da915Sopenharmony_ci if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != 546987da915Sopenharmony_ci NTFS_BLOCK_SIZE) { 547987da915Sopenharmony_ci ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " 548987da915Sopenharmony_ci "bytes of potential restart page.\n"); 549987da915Sopenharmony_ci goto err_out; 550987da915Sopenharmony_ci } 551987da915Sopenharmony_ci 552987da915Sopenharmony_ci /* 553987da915Sopenharmony_ci * A non-empty block means the logfile is not empty while an 554987da915Sopenharmony_ci * empty block after a non-empty block has been encountered 555987da915Sopenharmony_ci * means we are done. 556987da915Sopenharmony_ci */ 557987da915Sopenharmony_ci if (!ntfs_is_empty_recordp((le32*)kaddr)) 558987da915Sopenharmony_ci logfile_is_empty = FALSE; 559987da915Sopenharmony_ci else if (!logfile_is_empty) 560987da915Sopenharmony_ci break; 561987da915Sopenharmony_ci /* 562987da915Sopenharmony_ci * A log record page means there cannot be a restart page after 563987da915Sopenharmony_ci * this so no need to continue searching. 564987da915Sopenharmony_ci */ 565987da915Sopenharmony_ci if (ntfs_is_rcrd_recordp((le32*)kaddr)) 566987da915Sopenharmony_ci break; 567987da915Sopenharmony_ci /* If not a (modified by chkdsk) restart page, continue. */ 568987da915Sopenharmony_ci if (!ntfs_is_rstr_recordp((le32*)kaddr) && 569987da915Sopenharmony_ci !ntfs_is_chkd_recordp((le32*)kaddr)) { 570987da915Sopenharmony_ci if (!pos) 571987da915Sopenharmony_ci pos = NTFS_BLOCK_SIZE >> 1; 572987da915Sopenharmony_ci continue; 573987da915Sopenharmony_ci } 574987da915Sopenharmony_ci /* 575987da915Sopenharmony_ci * Check the (modified by chkdsk) restart page for consistency 576987da915Sopenharmony_ci * and get a copy of the complete multi sector transfer 577987da915Sopenharmony_ci * deprotected restart page. 578987da915Sopenharmony_ci */ 579987da915Sopenharmony_ci err = ntfs_check_and_load_restart_page(log_na, 580987da915Sopenharmony_ci (RESTART_PAGE_HEADER*)kaddr, pos, 581987da915Sopenharmony_ci !rstr1_ph ? &rstr1_ph : &rstr2_ph, 582987da915Sopenharmony_ci !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); 583987da915Sopenharmony_ci if (!err) { 584987da915Sopenharmony_ci /* 585987da915Sopenharmony_ci * If we have now found the first (modified by chkdsk) 586987da915Sopenharmony_ci * restart page, continue looking for the second one. 587987da915Sopenharmony_ci */ 588987da915Sopenharmony_ci if (!pos) { 589987da915Sopenharmony_ci pos = NTFS_BLOCK_SIZE >> 1; 590987da915Sopenharmony_ci continue; 591987da915Sopenharmony_ci } 592987da915Sopenharmony_ci /* 593987da915Sopenharmony_ci * We have now found the second (modified by chkdsk) 594987da915Sopenharmony_ci * restart page, so we can stop looking. 595987da915Sopenharmony_ci */ 596987da915Sopenharmony_ci break; 597987da915Sopenharmony_ci } 598987da915Sopenharmony_ci /* 599987da915Sopenharmony_ci * Error output already done inside the function. Note, we do 600987da915Sopenharmony_ci * not abort if the restart page was invalid as we might still 601987da915Sopenharmony_ci * find a valid one further in the file. 602987da915Sopenharmony_ci */ 603987da915Sopenharmony_ci if (err != EINVAL) 604987da915Sopenharmony_ci goto err_out; 605987da915Sopenharmony_ci /* Continue looking. */ 606987da915Sopenharmony_ci if (!pos) 607987da915Sopenharmony_ci pos = NTFS_BLOCK_SIZE >> 1; 608987da915Sopenharmony_ci } 609987da915Sopenharmony_ci if (kaddr) { 610987da915Sopenharmony_ci free(kaddr); 611987da915Sopenharmony_ci kaddr = NULL; 612987da915Sopenharmony_ci } 613987da915Sopenharmony_ci if (logfile_is_empty) { 614987da915Sopenharmony_ci NVolSetLogFileEmpty(vol); 615987da915Sopenharmony_ciis_empty: 616987da915Sopenharmony_ci ntfs_log_trace("Done. ($LogFile is empty.)\n"); 617987da915Sopenharmony_ci return TRUE; 618987da915Sopenharmony_ci } 619987da915Sopenharmony_ci if (!rstr1_ph) { 620987da915Sopenharmony_ci if (rstr2_ph) 621987da915Sopenharmony_ci ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); 622987da915Sopenharmony_ci ntfs_log_error("Did not find any restart pages in " 623987da915Sopenharmony_ci "$LogFile and it was not empty.\n"); 624987da915Sopenharmony_ci return FALSE; 625987da915Sopenharmony_ci } 626987da915Sopenharmony_ci /* If both restart pages were found, use the more recent one. */ 627987da915Sopenharmony_ci if (rstr2_ph) { 628987da915Sopenharmony_ci /* 629987da915Sopenharmony_ci * If the second restart area is more recent, switch to it. 630987da915Sopenharmony_ci * Otherwise just throw it away. 631987da915Sopenharmony_ci */ 632987da915Sopenharmony_ci if (rstr2_lsn > rstr1_lsn) { 633987da915Sopenharmony_ci ntfs_log_debug("Using second restart page as it is more " 634987da915Sopenharmony_ci "recent.\n"); 635987da915Sopenharmony_ci free(rstr1_ph); 636987da915Sopenharmony_ci rstr1_ph = rstr2_ph; 637987da915Sopenharmony_ci /* rstr1_lsn = rstr2_lsn; */ 638987da915Sopenharmony_ci } else { 639987da915Sopenharmony_ci ntfs_log_debug("Using first restart page as it is more " 640987da915Sopenharmony_ci "recent.\n"); 641987da915Sopenharmony_ci free(rstr2_ph); 642987da915Sopenharmony_ci } 643987da915Sopenharmony_ci rstr2_ph = NULL; 644987da915Sopenharmony_ci } 645987da915Sopenharmony_ci /* All consistency checks passed. */ 646987da915Sopenharmony_ci if (rp) 647987da915Sopenharmony_ci *rp = rstr1_ph; 648987da915Sopenharmony_ci else 649987da915Sopenharmony_ci free(rstr1_ph); 650987da915Sopenharmony_ci ntfs_log_trace("Done.\n"); 651987da915Sopenharmony_ci return TRUE; 652987da915Sopenharmony_cierr_out: 653987da915Sopenharmony_ci free(kaddr); 654987da915Sopenharmony_ci free(rstr1_ph); 655987da915Sopenharmony_ci free(rstr2_ph); 656987da915Sopenharmony_ci return FALSE; 657987da915Sopenharmony_ci} 658987da915Sopenharmony_ci 659987da915Sopenharmony_ci/** 660987da915Sopenharmony_ci * ntfs_is_logfile_clean - check in the journal if the volume is clean 661987da915Sopenharmony_ci * @log_na: ntfs attribute of loaded journal $LogFile to check 662987da915Sopenharmony_ci * @rp: copy of the current restart page 663987da915Sopenharmony_ci * 664987da915Sopenharmony_ci * Analyze the $LogFile journal and return TRUE if it indicates the volume was 665987da915Sopenharmony_ci * shutdown cleanly and FALSE if not. 666987da915Sopenharmony_ci * 667987da915Sopenharmony_ci * At present we only look at the two restart pages and ignore the log record 668987da915Sopenharmony_ci * pages. This is a little bit crude in that there will be a very small number 669987da915Sopenharmony_ci * of cases where we think that a volume is dirty when in fact it is clean. 670987da915Sopenharmony_ci * This should only affect volumes that have not been shutdown cleanly but did 671987da915Sopenharmony_ci * not have any pending, non-check-pointed i/o, i.e. they were completely idle 672987da915Sopenharmony_ci * at least for the five seconds preceding the unclean shutdown. 673987da915Sopenharmony_ci * 674987da915Sopenharmony_ci * This function assumes that the $LogFile journal has already been consistency 675987da915Sopenharmony_ci * checked by a call to ntfs_check_logfile() and in particular if the $LogFile 676987da915Sopenharmony_ci * is empty this function requires that NVolLogFileEmpty() is true otherwise an 677987da915Sopenharmony_ci * empty volume will be reported as dirty. 678987da915Sopenharmony_ci */ 679987da915Sopenharmony_ciBOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) 680987da915Sopenharmony_ci{ 681987da915Sopenharmony_ci RESTART_AREA *ra; 682987da915Sopenharmony_ci 683987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 684987da915Sopenharmony_ci /* An empty $LogFile must have been clean before it got emptied. */ 685987da915Sopenharmony_ci if (NVolLogFileEmpty(log_na->ni->vol)) { 686987da915Sopenharmony_ci ntfs_log_trace("$LogFile is empty\n"); 687987da915Sopenharmony_ci return TRUE; 688987da915Sopenharmony_ci } 689987da915Sopenharmony_ci if (!rp) { 690987da915Sopenharmony_ci ntfs_log_error("Restart page header is NULL\n"); 691987da915Sopenharmony_ci return FALSE; 692987da915Sopenharmony_ci } 693987da915Sopenharmony_ci if (!ntfs_is_rstr_record(rp->magic) && 694987da915Sopenharmony_ci !ntfs_is_chkd_record(rp->magic)) { 695987da915Sopenharmony_ci ntfs_log_error("Restart page buffer is invalid\n"); 696987da915Sopenharmony_ci return FALSE; 697987da915Sopenharmony_ci } 698987da915Sopenharmony_ci 699987da915Sopenharmony_ci ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 700987da915Sopenharmony_ci /* 701987da915Sopenharmony_ci * If the $LogFile has active clients, i.e. it is open, and we do not 702987da915Sopenharmony_ci * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, 703987da915Sopenharmony_ci * we assume there was an unclean shutdown. 704987da915Sopenharmony_ci */ 705987da915Sopenharmony_ci if (ra->client_in_use_list != LOGFILE_NO_CLIENT && 706987da915Sopenharmony_ci !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { 707987da915Sopenharmony_ci ntfs_log_error("The disk contains an unclean file system (%d, " 708987da915Sopenharmony_ci "%d).\n", le16_to_cpu(ra->client_in_use_list), 709987da915Sopenharmony_ci le16_to_cpu(ra->flags)); 710987da915Sopenharmony_ci return FALSE; 711987da915Sopenharmony_ci } 712987da915Sopenharmony_ci /* $LogFile indicates a clean shutdown. */ 713987da915Sopenharmony_ci ntfs_log_trace("$LogFile indicates a clean shutdown\n"); 714987da915Sopenharmony_ci return TRUE; 715987da915Sopenharmony_ci} 716987da915Sopenharmony_ci 717987da915Sopenharmony_ci/** 718987da915Sopenharmony_ci * ntfs_empty_logfile - empty the contents of the $LogFile journal 719987da915Sopenharmony_ci * @na: ntfs attribute of journal $LogFile to empty 720987da915Sopenharmony_ci * 721987da915Sopenharmony_ci * Empty the contents of the $LogFile journal @na and return 0 on success and 722987da915Sopenharmony_ci * -1 on error. 723987da915Sopenharmony_ci * 724987da915Sopenharmony_ci * This function assumes that the $LogFile journal has already been consistency 725987da915Sopenharmony_ci * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() 726987da915Sopenharmony_ci * has been used to ensure that the $LogFile is clean. 727987da915Sopenharmony_ci */ 728987da915Sopenharmony_ciint ntfs_empty_logfile(ntfs_attr *na) 729987da915Sopenharmony_ci{ 730987da915Sopenharmony_ci s64 pos, count; 731987da915Sopenharmony_ci char buf[NTFS_BUF_SIZE]; 732987da915Sopenharmony_ci 733987da915Sopenharmony_ci ntfs_log_trace("Entering.\n"); 734987da915Sopenharmony_ci 735987da915Sopenharmony_ci if (NVolLogFileEmpty(na->ni->vol)) 736987da915Sopenharmony_ci return 0; 737987da915Sopenharmony_ci 738987da915Sopenharmony_ci if (!NAttrNonResident(na)) { 739987da915Sopenharmony_ci errno = EIO; 740987da915Sopenharmony_ci ntfs_log_perror("Resident $LogFile $DATA attribute"); 741987da915Sopenharmony_ci return -1; 742987da915Sopenharmony_ci } 743987da915Sopenharmony_ci 744987da915Sopenharmony_ci memset(buf, -1, NTFS_BUF_SIZE); 745987da915Sopenharmony_ci 746987da915Sopenharmony_ci pos = 0; 747987da915Sopenharmony_ci while ((count = na->data_size - pos) > 0) { 748987da915Sopenharmony_ci 749987da915Sopenharmony_ci if (count > NTFS_BUF_SIZE) 750987da915Sopenharmony_ci count = NTFS_BUF_SIZE; 751987da915Sopenharmony_ci 752987da915Sopenharmony_ci count = ntfs_attr_pwrite(na, pos, count, buf); 753987da915Sopenharmony_ci if (count <= 0) { 754987da915Sopenharmony_ci ntfs_log_perror("Failed to reset $LogFile"); 755987da915Sopenharmony_ci if (count != -1) 756987da915Sopenharmony_ci errno = EIO; 757987da915Sopenharmony_ci return -1; 758987da915Sopenharmony_ci } 759987da915Sopenharmony_ci pos += count; 760987da915Sopenharmony_ci } 761987da915Sopenharmony_ci 762987da915Sopenharmony_ci NVolSetLogFileEmpty(na->ni->vol); 763987da915Sopenharmony_ci 764987da915Sopenharmony_ci return 0; 765987da915Sopenharmony_ci} 766