1/* 2 * ntfs_logfile.c - NTFS kernel journal handling. 3 * 4 * Copyright (c) 2006-2011 Anton Altaparmakov. All Rights Reserved. 5 * Portions Copyright (c) 2006-2011 Apple Inc. All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 16 * contributors may be used to endorse or promote products derived from this 17 * software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * ALTERNATIVELY, provided that this notice and licensing terms are retained in 31 * full, this file may be redistributed and/or modified under the terms of the 32 * GNU General Public License (GPL) Version 2, in which case the provisions of 33 * that version of the GPL will apply to you instead of the license terms 34 * above. You can obtain a copy of the GPL Version 2 at 35 * http://developer.apple.com/opensource/licenses/gpl-2.txt. 36 */ 37 38#include <sys/errno.h> 39#include <sys/ucred.h> 40#include <sys/ubc.h> 41 42#include <string.h> 43 44#include <libkern/libkern.h> 45#include <libkern/OSMalloc.h> 46 47#include <kern/debug.h> 48 49#include "ntfs.h" 50#include "ntfs_attr.h" 51#include "ntfs_debug.h" 52#include "ntfs_endian.h" 53#include "ntfs_inode.h" 54#include "ntfs_layout.h" 55#include "ntfs_logfile.h" 56#include "ntfs_mst.h" 57#include "ntfs_page.h" 58#include "ntfs_types.h" 59#include "ntfs_volume.h" 60 61/** 62 * ntfs_restart_page_header_is_valid - check the page header for consistency 63 * @ni: ntfs inode of $LogFile to which the restart page header belongs 64 * @rp: restart page header to check 65 * @pos: position in @ni at which the restart page header resides 66 * 67 * Check the restart page header @rp for consistency and return TRUE if it is 68 * consistent and FALSE otherwise. 69 * 70 * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 71 * require the full restart page. 72 */ 73static BOOL ntfs_restart_page_header_is_valid(ntfs_inode *ni, 74 RESTART_PAGE_HEADER *rp, s64 pos) 75{ 76 u32 logfile_system_page_size, logfile_log_page_size; 77 u16 ra_ofs, usa_count, usa_ofs, usa_end = 0; 78 BOOL have_usa = TRUE; 79 80 ntfs_debug("Entering."); 81 /* 82 * If the system or log page sizes are smaller than the ntfs block size 83 * or either is not a power of 2 we cannot handle this log file. 84 */ 85 logfile_system_page_size = le32_to_cpu(rp->system_page_size); 86 logfile_log_page_size = le32_to_cpu(rp->log_page_size); 87 if (logfile_system_page_size < NTFS_BLOCK_SIZE || 88 logfile_log_page_size < NTFS_BLOCK_SIZE || 89 logfile_system_page_size & 90 (logfile_system_page_size - 1) || 91 logfile_log_page_size & (logfile_log_page_size - 1)) { 92 ntfs_error(ni->vol->mp, "$LogFile uses unsupported page " 93 "size."); 94 return FALSE; 95 } 96 /* 97 * We must be either at !pos (1st restart page) or at pos = system page 98 * size (2nd restart page). 99 */ 100 if (pos && pos != logfile_system_page_size) { 101 ntfs_error(ni->vol->mp, "Found restart area in incorrect " 102 "position in $LogFile."); 103 return FALSE; 104 } 105 /* We only know how to handle version 1.1. */ 106 if (sle16_to_cpu(rp->major_ver) != 1 || 107 sle16_to_cpu(rp->minor_ver) != 1) { 108 ntfs_error(ni->vol->mp, "$LogFile version %d.%d is not " 109 "supported. (This driver supports version " 110 "1.1 only.)", (int)sle16_to_cpu(rp->major_ver), 111 (int)sle16_to_cpu(rp->minor_ver)); 112 return FALSE; 113 } 114 /* 115 * If chkdsk has been run the restart page may not be protected by an 116 * update sequence array. 117 */ 118 if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) { 119 have_usa = FALSE; 120 goto skip_usa_checks; 121 } 122 /* Verify the size of the update sequence array. */ 123 usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_SHIFT); 124 if (usa_count != le16_to_cpu(rp->usa_count)) { 125 ntfs_error(ni->vol->mp, "$LogFile restart page specifies " 126 "inconsistent update sequence array count."); 127 return FALSE; 128 } 129 /* Verify the position of the update sequence array. */ 130 usa_ofs = le16_to_cpu(rp->usa_ofs); 131 usa_end = usa_ofs + usa_count * sizeof(u16); 132 if (usa_ofs < sizeof(RESTART_PAGE_HEADER) || 133 usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) { 134 ntfs_error(ni->vol->mp, "$LogFile restart page specifies " 135 "inconsistent update sequence array offset."); 136 return FALSE; 137 } 138skip_usa_checks: 139 /* 140 * Verify the position of the restart area. It must be: 141 * - aligned to 8-byte boundary, 142 * - after the update sequence array, and 143 * - within the system page size. 144 */ 145 ra_ofs = le16_to_cpu(rp->restart_area_offset); 146 if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end : 147 ra_ofs < sizeof(RESTART_PAGE_HEADER)) || 148 ra_ofs > logfile_system_page_size) { 149 ntfs_error(ni->vol->mp, "$LogFile restart page specifies " 150 "inconsistent restart area offset."); 151 return FALSE; 152 } 153 /* 154 * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn 155 * set. 156 */ 157 if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { 158 ntfs_error(ni->vol->mp, "$LogFile restart page is not " 159 "modified by chkdsk but a chkdsk LSN is " 160 "specified."); 161 return FALSE; 162 } 163 ntfs_debug("Done."); 164 return TRUE; 165} 166 167/** 168 * ntfs_restart_area_is_valid - check the restart area for consistency 169 * @ni: ntfs inode of $LogFile to which the restart page belongs 170 * @rp: restart page whose restart area to check 171 * 172 * Check the restart area of the restart page @rp for consistency and return 173 * TRUE if it is consistent and FALSE otherwise. 174 * 175 * This function assumes that the restart page header has already been 176 * consistency checked. 177 * 178 * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 179 * require the full restart page. 180 */ 181static BOOL ntfs_restart_area_is_valid(ntfs_inode *ni, RESTART_PAGE_HEADER *rp) 182{ 183 u64 file_size; 184 RESTART_AREA *ra; 185 u16 ra_ofs, ra_len, ca_ofs; 186 u8 fs_bits; 187 188 ntfs_debug("Entering."); 189 ra_ofs = le16_to_cpu(rp->restart_area_offset); 190 ra = (RESTART_AREA*)((u8*)rp + ra_ofs); 191 /* 192 * Everything before ra->file_size must be before the first word 193 * protected by an update sequence number. This ensures that it is 194 * safe to access ra->client_array_offset. 195 */ 196 if (ra_ofs + offsetof(RESTART_AREA, file_size) > 197 NTFS_BLOCK_SIZE - sizeof(u16)) { 198 ntfs_error(ni->vol->mp, "$LogFile restart area specifies " 199 "inconsistent file offset."); 200 return FALSE; 201 } 202 /* 203 * Now that we can access ra->client_array_offset, make sure everything 204 * up to the log client array is before the first word protected by an 205 * update sequence number. This ensures we can access all of the 206 * restart area elements safely. Also, the client array offset must be 207 * aligned to an 8-byte boundary. 208 */ 209 ca_ofs = le16_to_cpu(ra->client_array_offset); 210 if (((ca_ofs + 7) & ~7) != ca_ofs || 211 ra_ofs + ca_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) { 212 ntfs_error(ni->vol->mp, "$LogFile restart area specifies " 213 "inconsistent client array offset."); 214 return FALSE; 215 } 216 /* 217 * The restart area must end within the system page size both when 218 * calculated manually and as specified by ra->restart_area_length. 219 * Also, the calculated length must not exceed the specified length. 220 */ 221 ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * 222 sizeof(LOG_CLIENT_RECORD); 223 if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) || 224 ra_ofs + le16_to_cpu(ra->restart_area_length) > 225 le32_to_cpu(rp->system_page_size) || 226 ra_len > le16_to_cpu(ra->restart_area_length)) { 227 ntfs_error(ni->vol->mp, "$LogFile restart area is out of " 228 "bounds of the system page size specified by " 229 "the restart page header and/or the specified " 230 "restart area length is inconsistent."); 231 return FALSE; 232 } 233 /* 234 * The ra->client_free_list and ra->client_in_use_list must be either 235 * LOGFILE_NO_CLIENT or less than ra->log_clients or they are 236 * overflowing the client array. 237 */ 238 if ((ra->client_free_list != LOGFILE_NO_CLIENT && 239 le16_to_cpu(ra->client_free_list) >= 240 le16_to_cpu(ra->log_clients)) || 241 (ra->client_in_use_list != LOGFILE_NO_CLIENT && 242 le16_to_cpu(ra->client_in_use_list) >= 243 le16_to_cpu(ra->log_clients))) { 244 ntfs_error(ni->vol->mp, "$LogFile restart area specifies " 245 "overflowing client free and/or in use lists."); 246 return FALSE; 247 } 248 /* 249 * Check ra->seq_number_bits against ra->file_size for consistency. 250 * We cannot just use ffs() because the file size is not a power of 2. 251 */ 252 file_size = (u64)sle64_to_cpu(ra->file_size); 253 fs_bits = 0; 254 while (file_size) { 255 file_size >>= 1; 256 fs_bits++; 257 } 258 if (le32_to_cpu(ra->seq_number_bits) != (unsigned)(67 - fs_bits)) { 259 ntfs_error(ni->vol->mp, "$LogFile restart area specifies " 260 "inconsistent sequence number bits."); 261 return FALSE; 262 } 263 /* The log record header length must be a multiple of 8. */ 264 if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) != 265 le16_to_cpu(ra->log_record_header_length)) { 266 ntfs_error(ni->vol->mp, "$LogFile restart area specifies " 267 "inconsistent log record header length."); 268 return FALSE; 269 } 270 /* Dito for the log page data offset. */ 271 if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) != 272 le16_to_cpu(ra->log_page_data_offset)) { 273 ntfs_error(ni->vol->mp, "$LogFile restart area specifies " 274 "inconsistent log page data offset."); 275 return FALSE; 276 } 277 ntfs_debug("Done."); 278 return TRUE; 279} 280 281/** 282 * ntfs_log_client_array_is_consistent - consistency check the log client array 283 * @ni: ntfs inode of $LogFile to which the restart page belongs 284 * @rp: restart page whose log client array to check 285 * 286 * Check the log client array of the restart page @rp for consistency and 287 * return TRUE if it is consistent and FALSE otherwise. 288 * 289 * This function assumes that the restart page header and the restart area have 290 * already been consistency checked. 291 * 292 * Unlike ntfs_restart_page_header_is_valid() and ntfs_restart_area_is_valid(), 293 * this function needs @rp->system_page_size bytes in @rp, i.e. it requires the 294 * full restart page and the page must be multi sector transfer deprotected. 295 */ 296static BOOL ntfs_log_client_array_is_consistent(ntfs_inode *ni, 297 RESTART_PAGE_HEADER *rp) 298{ 299 RESTART_AREA *ra; 300 LOG_CLIENT_RECORD *ca, *cr; 301 u16 nr_clients, idx; 302 BOOL in_free_list, idx_is_first; 303 304 ntfs_debug("Entering."); 305 ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 306 ca = (LOG_CLIENT_RECORD*)((u8*)ra + 307 le16_to_cpu(ra->client_array_offset)); 308 /* 309 * Check the ra->client_free_list first and then check the 310 * ra->client_in_use_list. Check each of the log client records in 311 * each of the lists and check that the array does not overflow the 312 * ra->log_clients value. Also keep track of the number of records 313 * visited as there cannot be more than ra->log_clients records and 314 * that way we detect eventual loops in within a list. 315 */ 316 nr_clients = le16_to_cpu(ra->log_clients); 317 idx = le16_to_cpu(ra->client_free_list); 318 in_free_list = TRUE; 319check_list: 320 for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--, 321 idx = le16_to_cpu(cr->next_client)) { 322 if (!nr_clients || idx >= le16_to_cpu(ra->log_clients)) 323 goto err; 324 /* Set @cr to the current log client record. */ 325 cr = ca + idx; 326 /* The first log client record must not have a prev_client. */ 327 if (idx_is_first) { 328 if (cr->prev_client != LOGFILE_NO_CLIENT) 329 goto err; 330 idx_is_first = FALSE; 331 } 332 } 333 /* Switch to and check the in use list if we just did the free list. */ 334 if (in_free_list) { 335 in_free_list = FALSE; 336 idx = le16_to_cpu(ra->client_in_use_list); 337 goto check_list; 338 } 339 ntfs_debug("Done."); 340 return TRUE; 341err: 342 ntfs_error(ni->vol->mp, "$LogFile log client array is corrupt."); 343 return FALSE; 344} 345 346/** 347 * ntfs_restart_page_load - load and check the restart page for consistency 348 * @ni: ntfs inode of $LogFile to which the restart page belongs 349 * @rp: restart page to check 350 * @pos: position in @ni at which the restart page resides 351 * @wrp: [OUT] copy of the multi sector transfer deprotected restart page 352 * @lsn: [OUT] set to the current logfile lsn on success 353 * 354 * Check the restart page @rp for consistency and return 0 if it is consistent 355 * and errno otherwise. The restart page may have been modified by chkdsk in 356 * which case its magic is CHKD instead of RSTR. 357 * 358 * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not 359 * require the full restart page. 360 * 361 * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a 362 * copy of the complete multi sector transfer deprotected page. On failure, 363 * *@wrp is undefined. 364 * 365 * Simillarly, if @lsn is not NULL, on success *@lsn will be set to the current 366 * logfile lsn according to this restart page. On failure, *@lsn is undefined. 367 * 368 * The following error codes are defined: 369 * EINVAL - The restart page is inconsistent. 370 * ENOMEM - Not enough memory to load the restart page. 371 * EIO - Failed to read from $LogFile. 372 */ 373static errno_t ntfs_restart_page_load(ntfs_inode *ni, RESTART_PAGE_HEADER *rp, 374 s64 pos, RESTART_PAGE_HEADER **wrp, LSN *lsn) 375{ 376 RESTART_AREA *ra; 377 RESTART_PAGE_HEADER *trp; 378 unsigned size; 379 errno_t err; 380 381 ntfs_debug("Entering."); 382 /* Check the restart page header for consistency. */ 383 if (!ntfs_restart_page_header_is_valid(ni, rp, pos)) { 384 /* Error output already done inside the function. */ 385 return EINVAL; 386 } 387 /* Check the restart area for consistency. */ 388 if (!ntfs_restart_area_is_valid(ni, rp)) { 389 /* Error output already done inside the function. */ 390 return EINVAL; 391 } 392 ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 393 /* 394 * Allocate a buffer to store the whole restart page so we can multi 395 * sector transfer deprotect it. 396 */ 397 trp = OSMalloc(le32_to_cpu(rp->system_page_size), ntfs_malloc_tag); 398 if (!trp) { 399 ntfs_error(ni->vol->mp, "Failed to allocate memory for " 400 "$LogFile restart page buffer."); 401 return ENOMEM; 402 } 403 /* 404 * Read the whole of the restart page into the buffer. If it fits 405 * completely inside @rp, just copy it from there. Otherwise map all 406 * the required pages and copy the data from them. 407 */ 408 size = PAGE_SIZE - ((unsigned)pos & PAGE_MASK); 409 if (size >= le32_to_cpu(rp->system_page_size)) 410 memcpy(trp, rp, le32_to_cpu(rp->system_page_size)); 411 else { 412 upl_t upl; 413 upl_page_info_array_t pl; 414 u8 *kaddr; 415 unsigned have_read, to_read; 416 417 /* First copy what we already have in @rp. */ 418 memcpy(trp, rp, size); 419 /* Copy the remaining data one page at a time. */ 420 have_read = size; 421 to_read = le32_to_cpu(rp->system_page_size) - size; 422 do { 423 pos += size; 424 if ((unsigned)pos & PAGE_MASK) 425 panic("%s(): pos + size is not PAGE_SIZE " 426 "aligned\n", __FUNCTION__); 427 err = ntfs_page_map(ni, pos, &upl, &pl, &kaddr, FALSE); 428 if (err) { 429 ntfs_error(ni->vol->mp, "Error reading " 430 "$LogFile."); 431 if (err != EIO && err != ENOMEM) 432 err = EIO; 433 goto err; 434 } 435 size = PAGE_SIZE; 436 if (size > to_read) 437 size = to_read; 438 memcpy((u8*)trp + have_read, kaddr, size); 439 ntfs_page_unmap(ni, upl, pl, FALSE); 440 have_read += size; 441 to_read -= size; 442 } while (to_read > 0); 443 } 444 /* 445 * Perform the multi sector transfer deprotection on the buffer if the 446 * restart page is protected. 447 */ 448 if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) && 449 ntfs_mst_fixup_post_read((NTFS_RECORD*)trp, 450 le32_to_cpu(rp->system_page_size))) { 451 /* 452 * A multi sector tranfer error was detected. We only need to 453 * abort if the restart page contents exceed the multi sector 454 * transfer fixup of the first sector. 455 */ 456 if (le16_to_cpu(rp->restart_area_offset) + 457 le16_to_cpu(ra->restart_area_length) > 458 NTFS_BLOCK_SIZE - sizeof(u16)) { 459 ntfs_error(ni->vol->mp, "Multi sector transfer error " 460 "detected in $LogFile restart page."); 461 err = EINVAL; 462 goto err; 463 } 464 } 465 /* 466 * If the restart page is modified by chkdsk or there are no active 467 * logfile clients, the logfile is consistent. Otherwise, need to 468 * check the log client records for consistency, too. 469 */ 470 err = 0; 471 if (ntfs_is_rstr_record(rp->magic) && 472 ra->client_in_use_list != LOGFILE_NO_CLIENT) { 473 if (!ntfs_log_client_array_is_consistent(ni, trp)) { 474 err = EINVAL; 475 goto err; 476 } 477 } 478 if (lsn) { 479 if (ntfs_is_rstr_record(rp->magic)) 480 *lsn = sle64_to_cpu(ra->current_lsn); 481 else /* if (ntfs_is_chkd_record(rp->magic)) */ 482 *lsn = sle64_to_cpu(rp->chkdsk_lsn); 483 } 484 ntfs_debug("Done."); 485 if (wrp) 486 *wrp = trp; 487 else { 488err: 489 OSFree(trp, le32_to_cpu(trp->system_page_size), 490 ntfs_malloc_tag); 491 } 492 return err; 493} 494 495/** 496 * ntfs_logfile_check - check the $LogFile journal for consistency 497 * @ni: ntfs inode of loaded $LogFile journal to check 498 * @rp: [OUT] on success this is a copy of the current restart page 499 * 500 * Check the $LogFile journal for consistency and return 0 if it is consistent 501 * and EINVAL if not. On success, the current restart page is returned in 502 * *@rp. Caller must call OSFree(*@rp, le32_to_cpu(*@rp->system_page_size), 503 * ntfs_malloc_tag) when finished with it. 504 * 505 * On error the error code (not EINVAL) is returned. 506 * 507 * At present we only check the two restart pages and ignore the log record 508 * pages. 509 * 510 * Note that the MstProtected flag is not set on the $LogFile inode and hence 511 * when reading pages they are not deprotected. This is because we do not know 512 * if the $LogFile was created on a system with a different page size to ours 513 * yet and mst deprotection would fail if our page size is smaller. 514 */ 515errno_t ntfs_logfile_check(ntfs_inode *ni, RESTART_PAGE_HEADER **rp) 516{ 517 s64 size, pos, ppos; 518 LSN rstr1_lsn, rstr2_lsn; 519 ntfs_volume *vol; 520 RESTART_PAGE_HEADER *rstr1_ph, *rstr2_ph; 521 upl_t upl; 522 upl_page_info_array_t pl; 523 u8 *paddr, *kaddr; 524 unsigned log_page_size, log_page_mask; 525 errno_t err; 526 BOOL logfile_is_empty = TRUE; 527 u8 log_page_bits; 528 529 ntfs_debug("Entering."); 530 vol = ni->vol; 531 /* 532 * If the $LogFile is empty we will return success but we will not be 533 * returning anything in *@rp thus set it to NULL now to cover for all 534 * possible cases of us returning without allocating *@rp. 535 */ 536 if (rp) 537 *rp = NULL; 538 /* An empty $LogFile must have been clean before it got emptied. */ 539 if (NVolLogFileEmpty(vol)) 540 goto is_empty; 541 err = vnode_get(ni->vn); 542 if (err) { 543 if (err == EINVAL) 544 err = EIO; 545 ntfs_error(vol->mp, "Failed to get vnode for $LogFile."); 546 return err; 547 } 548 lck_rw_lock_shared(&ni->lock); 549 lck_spin_lock(&ni->size_lock); 550 size = ni->data_size; 551 lck_spin_unlock(&ni->size_lock); 552 /* Make sure the file does not exceed the maximum allowed size. */ 553 if (size > (s64)NtfsMaxLogFileSize) 554 size = NtfsMaxLogFileSize; 555 /* 556 * Truncate size to a multiple of the page cache size or the default 557 * log page size if the page cache size is between the default log page 558 * log page size if the page cache size is between the default log page 559 * size and twice that. 560 */ 561 if (PAGE_SIZE >= NtfsDefaultLogPageSize && 562 PAGE_SIZE <= NtfsDefaultLogPageSize * 2) 563 log_page_size = NtfsDefaultLogPageSize; 564 else 565 log_page_size = PAGE_SIZE; 566 log_page_mask = log_page_size - 1; 567 log_page_bits = ffs(log_page_size) - 1; 568 size &= ~(s64)(log_page_size - 1); 569 /* 570 * Ensure the log file is big enough to store at least the two restart 571 * pages and the minimum number of log record pages. 572 */ 573 if (size < log_page_size * 2 || (size - log_page_size * 2) >> 574 log_page_bits < NtfsMinLogRecordPages) { 575 ntfs_error(vol->mp, "$LogFile is too small."); 576 lck_rw_unlock_shared(&ni->lock); 577 (void)vnode_put(ni->vn); 578 return EINVAL; 579 } 580 /* 581 * Read through the file looking for a restart page. Since the restart 582 * page header is at the beginning of a page we only need to search at 583 * what could be the beginning of a page (for each page size) rather 584 * than scanning the whole file byte by byte. If all potential places 585 * contain empty and uninitialzed records, the log file can be assumed 586 * to be empty. 587 */ 588 upl = NULL; 589 rstr1_ph = rstr2_ph = NULL; 590 for (pos = ppos = 0; pos < size; pos <<= 1) { 591 if (!upl || (s64)(pos & ~PAGE_MASK_64) != ppos) { 592 if (upl) 593 ntfs_page_unmap(ni, upl, pl, FALSE); 594 ppos = pos; 595 err = ntfs_page_map(ni, pos, &upl, &pl, &paddr, FALSE); 596 if (err) { 597 if (err != EIO && err != ENOMEM) 598 err = EIO; 599 ntfs_error(vol->mp, "Error reading $LogFile."); 600 goto err; 601 } 602 } 603 kaddr = paddr + (pos & PAGE_MASK_64); 604 /* 605 * A non-empty block means the logfile is not empty while an 606 * empty block after a non-empty block has been encountered 607 * means we are done. 608 */ 609 if (!ntfs_is_empty_recordp((le32*)kaddr)) 610 logfile_is_empty = FALSE; 611 else { 612 if (logfile_is_empty) { 613 /* 614 * All records so far have been empty, 615 * continue. 616 */ 617 if (!pos) 618 pos = NTFS_BLOCK_SIZE / 2; 619 continue; 620 } 621 /* 622 * This is the first empty record and at least one 623 * non-empty record has been found previously. We are 624 * done. 625 */ 626 break; 627 } 628 /* 629 * A log record page means there cannot be a restart page after 630 * this so no need to continue searching. 631 */ 632 if (ntfs_is_rcrd_recordp((le32*)kaddr)) 633 break; 634 /* If not a (modified by chkdsk) restart page, continue. */ 635 if (!ntfs_is_rstr_recordp((le32*)kaddr) && 636 !ntfs_is_chkd_recordp((le32*)kaddr)) { 637 if (!pos) 638 pos = NTFS_BLOCK_SIZE / 2; 639 continue; 640 } 641 /* 642 * Check the (modified by chkdsk) restart page for consistency 643 * and get a copy of the complete multi sector transfer 644 * deprotected restart page. 645 */ 646 err = ntfs_restart_page_load(ni, 647 (RESTART_PAGE_HEADER*)kaddr, pos, 648 !rstr1_ph ? &rstr1_ph : &rstr2_ph, 649 !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); 650 if (!err) { 651 /* 652 * If we have now found the first (modified by chkdsk) 653 * restart page, continue looking for the second one. 654 */ 655 if (!pos) { 656 pos = NTFS_BLOCK_SIZE / 2; 657 continue; 658 } 659 /* 660 * We have now found the second (modified by chkdsk) 661 * restart page, so we can stop looking. 662 */ 663 break; 664 } 665 /* 666 * Error output already done inside the function. Note, we do 667 * not abort if the restart page was invalid as we might still 668 * find a valid one further in the file. 669 */ 670 if (err != EINVAL) { 671 ntfs_page_unmap(ni, upl, pl, FALSE); 672 goto err; 673 } 674 /* Continue looking. */ 675 if (!pos) 676 pos = NTFS_BLOCK_SIZE / 2; 677 } 678 if (upl) 679 ntfs_page_unmap(ni, upl, pl, FALSE); 680 lck_rw_unlock_shared(&ni->lock); 681 (void)vnode_put(ni->vn); 682 if (logfile_is_empty) { 683 NVolSetLogFileEmpty(vol); 684is_empty: 685 ntfs_debug("Done. ($LogFile is empty.)"); 686 return 0; 687 } 688 if (!rstr1_ph) { 689 if (rstr2_ph) 690 panic("%s(): !rstr1_ph but rstr2_ph\n", __FUNCTION__); 691 ntfs_error(vol->mp, "Did not find any restart pages in " 692 "$LogFile and it was not empty."); 693 return EINVAL; 694 } 695 /* If both restart pages were found, use the more recent one. */ 696 if (rstr2_ph) { 697 /* 698 * If the second restart area is more recent, switch to it. 699 * Otherwise just throw it away. 700 */ 701 if (rstr2_lsn > rstr1_lsn) { 702 ntfs_debug("Using second restart page as it is more " 703 "recent."); 704 OSFree(rstr1_ph, le32_to_cpu( 705 rstr1_ph->system_page_size), 706 ntfs_malloc_tag); 707 rstr1_ph = rstr2_ph; 708 /* rstr1_lsn = rstr2_lsn; */ 709 } else { 710 ntfs_debug("Using first restart page as it is more " 711 "recent."); 712 OSFree(rstr2_ph, le32_to_cpu( 713 rstr2_ph->system_page_size), 714 ntfs_malloc_tag); 715 } 716 rstr2_ph = NULL; 717 } 718 /* All consistency checks passed. */ 719 if (rp) 720 *rp = rstr1_ph; 721 else 722 OSFree(rstr1_ph, le32_to_cpu(rstr1_ph->system_page_size), 723 ntfs_malloc_tag); 724 ntfs_debug("Done."); 725 return 0; 726err: 727 lck_rw_unlock_shared(&ni->lock); 728 (void)vnode_put(ni->vn); 729 if (rstr1_ph) 730 OSFree(rstr1_ph, le32_to_cpu(rstr1_ph->system_page_size), 731 ntfs_malloc_tag); 732 return err; 733} 734 735/** 736 * ntfs_logfile_is_clean - check in the journal of the volume is clean 737 * @ni: ntfs inode of loaded $LogFile journal to check 738 * @rp: copy of the current restart page 739 * 740 * Analyze the $LogFile journal and return TRUE if it indicates the volume was 741 * shutdown cleanly and FALSE if not. 742 * 743 * At present we only look at the two restart pages and ignore the log record 744 * pages. This is a little bit crude in that there will be a very small number 745 * of cases where we think that a volume is dirty when in fact it is clean. 746 * This should only affect volumes that have not been shutdown cleanly but did 747 * not have any pending, non-check-pointed i/o, i.e. they were completely idle 748 * at least for the five seconds preceeding the unclean shutdown. 749 * 750 * This function assumes that the $LogFile journal has already been consistency 751 * checked by a call to ntfs_check_logfile() and in particular if the $LogFile 752 * is empty this function requires that NVolLogFileEmpty() is TRUE otherwise an 753 * empty volume will be reported as dirty. 754 */ 755BOOL ntfs_logfile_is_clean(ntfs_inode *ni, const RESTART_PAGE_HEADER *rp) 756{ 757 ntfs_volume *vol = ni->vol; 758 RESTART_AREA *ra; 759 760 ntfs_debug("Entering."); 761 /* An empty $LogFile must have been clean before it got emptied. */ 762 if (NVolLogFileEmpty(vol)) { 763 ntfs_debug("Done. ($LogFile is empty.)"); 764 return TRUE; 765 } 766 if (!rp) 767 panic("%s(): !rp\n", __FUNCTION__); 768 if (!ntfs_is_rstr_record(rp->magic) && 769 !ntfs_is_chkd_record(rp->magic)) { 770 ntfs_error(vol->mp, "Restart page buffer is invalid. This is " 771 "probably a bug in that the $LogFile should " 772 "have been consistency checked before calling " 773 "this function."); 774 return FALSE; 775 } 776 ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 777 /* 778 * If the $LogFile has active clients, i.e. it is open, and we do not 779 * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, 780 * we assume there was an unclean shutdown. 781 */ 782 if (ra->client_in_use_list != LOGFILE_NO_CLIENT && 783 !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { 784 ntfs_debug("Done. $LogFile indicates a dirty shutdown."); 785 return FALSE; 786 } 787 /* $LogFile indicates a clean shutdown. */ 788 ntfs_debug("Done. $LogFile indicates a clean shutdown."); 789 return TRUE; 790} 791 792/** 793 * ntfs_logfile_empty - empty the contents of the $LogFile journal 794 * @ni: ntfs inode of loaded $LogFile journal to empty 795 * 796 * Empty the contents of the $LogFile journal @ni. 797 * 798 * Return 0 on success and errno on error. 799 * 800 * This function assumes that the $LogFile journal has already been consistency 801 * checked by a call to ntfs_logfile_check() and that ntfs_logfile_is_clean() 802 * has been used to ensure that the $LogFile is clean. 803 */ 804errno_t ntfs_logfile_empty(ntfs_inode *ni) 805{ 806 ntfs_volume *vol = ni->vol; 807 808 ntfs_debug("Entering."); 809 if (!NVolLogFileEmpty(vol)) { 810 s64 data_size; 811 errno_t err; 812 813 err = vnode_get(ni->vn); 814 if (err) { 815 ntfs_error(vol->mp, "Failed to get vnode for " 816 "$LogFile."); 817 return err; 818 } 819 lck_rw_lock_shared(&ni->lock); 820 lck_spin_lock(&ni->size_lock); 821 data_size = ni->data_size; 822 lck_spin_unlock(&ni->size_lock); 823 err = ntfs_attr_set(ni, 0, data_size, 0xff); 824 lck_rw_unlock_shared(&ni->lock); 825 (void)vnode_put(ni->vn); 826 if (err) { 827 ntfs_error(vol->mp, "Failed to fill $LogFile with " 828 "0xff bytes (error code %d).", err); 829 return err; 830 } 831 /* Set the flag so we do not have to do it again on remount. */ 832 NVolSetLogFileEmpty(vol); 833 } 834 ntfs_debug("Done."); 835 return 0; 836} 837