📄 logfile.c
字号:
} /* * Perform the multi sector transfer deprotection on the buffer if the * restart page is protected. */ if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count)) && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp, le32_to_cpu(rp->system_page_size))) { /* * A multi sector tranfer error was detected. We only need to * abort if the restart page contents exceed the multi sector * transfer fixup of the first sector. */ if (le16_to_cpu(rp->restart_area_offset) + le16_to_cpu(ra->restart_area_length) > NTFS_BLOCK_SIZE - (int)sizeof(u16)) { ntfs_log_error("Multi sector transfer error " "detected in $LogFile restart page.\n"); err = EINVAL; goto err_out; } } /* * If the restart page is modified by chkdsk or there are no active * logfile clients, the logfile is consistent. Otherwise, need to * check the log client records for consistency, too. */ err = 0; if (ntfs_is_rstr_record(rp->magic) && ra->client_in_use_list != LOGFILE_NO_CLIENT) { if (!ntfs_check_log_client_array(trp)) { err = EINVAL; goto err_out; } } if (lsn) { if (ntfs_is_rstr_record(rp->magic)) *lsn = sle64_to_cpu(ra->current_lsn); else /* if (ntfs_is_chkd_record(rp->magic)) */ *lsn = sle64_to_cpu(rp->chkdsk_lsn); } ntfs_log_trace("Done.\n"); if (wrp) *wrp = trp; else {err_out: free(trp); } return err;}/** * ntfs_check_logfile - check in the journal if the volume is consistent * @log_na: ntfs attribute of loaded journal $LogFile to check * @rp: [OUT] on success this is a copy of the current restart page * * Check the $LogFile journal for consistency and return TRUE if it is * consistent and FALSE if not. On success, the current restart page is * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it. * * At present we only check the two restart pages and ignore the log record * pages. * * Note that the MstProtected flag is not set on the $LogFile inode and hence * when reading pages they are not deprotected. This is because we do not know * if the $LogFile was created on a system with a different page size to ours * yet and mst deprotection would fail if our page size is smaller. */BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp){ s64 size, pos; LSN rstr1_lsn, rstr2_lsn; ntfs_volume *vol = log_na->ni->vol; u8 *kaddr = NULL; RESTART_PAGE_HEADER *rstr1_ph = NULL; RESTART_PAGE_HEADER *rstr2_ph = NULL; int log_page_size, log_page_mask, err; BOOL logfile_is_empty = TRUE; u8 log_page_bits; ntfs_log_trace("Entering.\n"); /* An empty $LogFile must have been clean before it got emptied. */ if (NVolLogFileEmpty(vol)) goto is_empty; size = log_na->data_size; /* Make sure the file doesn't exceed the maximum allowed size. */ if (size > (s64)MaxLogFileSize) size = MaxLogFileSize; log_page_size = DefaultLogPageSize; log_page_mask = log_page_size - 1; /* * Use generic_ffs() instead of ffs() to enable the compiler to * optimize log_page_size and log_page_bits into constants. */ log_page_bits = ffs(log_page_size) - 1; size &= ~(log_page_size - 1); /* * Ensure the log file is big enough to store at least the two restart * pages and the minimum number of log record pages. */ if (size < log_page_size * 2 || (size - log_page_size * 2) >> log_page_bits < MinLogRecordPages) { ntfs_log_error("$LogFile is too small.\n"); return FALSE; } /* Allocate memory for restart page. */ kaddr = malloc(NTFS_BLOCK_SIZE); if (!kaddr) { ntfs_log_error("Not enough memory.\n"); return FALSE; } /* * Read through the file looking for a restart page. Since the restart * page header is at the beginning of a page we only need to search at * what could be the beginning of a page (for each page size) rather * than scanning the whole file byte by byte. If all potential places * contain empty and uninitialized records, the log file can be assumed * to be empty. */ for (pos = 0; pos < size; pos <<= 1) { /* * Read first NTFS_BLOCK_SIZE bytes of potential restart page. */ if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) != NTFS_BLOCK_SIZE) { ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE " "bytes of potential restart page.\n"); goto err_out; } /* * A non-empty block means the logfile is not empty while an * empty block after a non-empty block has been encountered * means we are done. */ if (!ntfs_is_empty_recordp((le32*)kaddr)) logfile_is_empty = FALSE; else if (!logfile_is_empty) break; /* * A log record page means there cannot be a restart page after * this so no need to continue searching. */ if (ntfs_is_rcrd_recordp((le32*)kaddr)) break; /* If not a (modified by chkdsk) restart page, continue. */ if (!ntfs_is_rstr_recordp((le32*)kaddr) && !ntfs_is_chkd_recordp((le32*)kaddr)) { if (!pos) pos = NTFS_BLOCK_SIZE >> 1; continue; } /* * Check the (modified by chkdsk) restart page for consistency * and get a copy of the complete multi sector transfer * deprotected restart page. */ err = ntfs_check_and_load_restart_page(log_na, (RESTART_PAGE_HEADER*)kaddr, pos, !rstr1_ph ? &rstr1_ph : &rstr2_ph, !rstr1_ph ? &rstr1_lsn : &rstr2_lsn); if (!err) { /* * If we have now found the first (modified by chkdsk) * restart page, continue looking for the second one. */ if (!pos) { pos = NTFS_BLOCK_SIZE >> 1; continue; } /* * We have now found the second (modified by chkdsk) * restart page, so we can stop looking. */ break; } /* * Error output already done inside the function. Note, we do * not abort if the restart page was invalid as we might still * find a valid one further in the file. */ if (err != EINVAL) goto err_out; /* Continue looking. */ if (!pos) pos = NTFS_BLOCK_SIZE >> 1; } if (kaddr) { free(kaddr); kaddr = NULL; } if (logfile_is_empty) { NVolSetLogFileEmpty(vol);is_empty: ntfs_log_trace("Done. ($LogFile is empty.)\n"); return TRUE; } if (!rstr1_ph) { if (rstr2_ph) ntfs_log_error("BUG: rstr2_ph isn't NULL!\n"); ntfs_log_error("Did not find any restart pages in " "$LogFile and it was not empty.\n"); return FALSE; } /* If both restart pages were found, use the more recent one. */ if (rstr2_ph) { /* * If the second restart area is more recent, switch to it. * Otherwise just throw it away. */ if (rstr2_lsn > rstr1_lsn) { ntfs_log_debug("Using second restart page as it is more " "recent.\n"); free(rstr1_ph); rstr1_ph = rstr2_ph; /* rstr1_lsn = rstr2_lsn; */ } else { ntfs_log_debug("Using first restart page as it is more " "recent.\n"); free(rstr2_ph); } rstr2_ph = NULL; } /* All consistency checks passed. */ if (rp) *rp = rstr1_ph; else free(rstr1_ph); ntfs_log_trace("Done.\n"); return TRUE;err_out: free(kaddr); free(rstr1_ph); free(rstr2_ph); return FALSE;}/** * ntfs_is_logfile_clean - check in the journal if the volume is clean * @log_na: ntfs attribute of loaded journal $LogFile to check * @rp: copy of the current restart page * * Analyze the $LogFile journal and return TRUE if it indicates the volume was * shutdown cleanly and FALSE if not. * * At present we only look at the two restart pages and ignore the log record * pages. This is a little bit crude in that there will be a very small number * of cases where we think that a volume is dirty when in fact it is clean. * This should only affect volumes that have not been shutdown cleanly but did * not have any pending, non-check-pointed i/o, i.e. they were completely idle * at least for the five seconds preceding the unclean shutdown. * * This function assumes that the $LogFile journal has already been consistency * checked by a call to ntfs_check_logfile() and in particular if the $LogFile * is empty this function requires that NVolLogFileEmpty() is true otherwise an * empty volume will be reported as dirty. */BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp){ RESTART_AREA *ra; ntfs_log_trace("Entering.\n"); /* An empty $LogFile must have been clean before it got emptied. */ if (NVolLogFileEmpty(log_na->ni->vol)) { ntfs_log_trace("Done. ($LogFile is empty.)\n"); return TRUE; } if (!rp) { ntfs_log_error("Restart page header is NULL.\n"); return FALSE; } if (!ntfs_is_rstr_record(rp->magic) && !ntfs_is_chkd_record(rp->magic)) { ntfs_log_error("Restart page buffer is invalid. This is " "probably a bug in that the $LogFile should " "have been consistency checked before calling " "this function.\n"); return FALSE; } ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); /* * If the $LogFile has active clients, i.e. it is open, and we do not * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags, * we assume there was an unclean shutdown. */ if (ra->client_in_use_list != LOGFILE_NO_CLIENT && !(ra->flags & RESTART_VOLUME_IS_CLEAN)) { ntfs_log_debug("Done. $LogFile indicates a dirty shutdown.\n"); return FALSE; } /* $LogFile indicates a clean shutdown. */ ntfs_log_trace("Done. $LogFile indicates a clean shutdown.\n"); return TRUE;}/** * ntfs_empty_logfile - empty the contents of the $LogFile journal * @na: ntfs attribute of journal $LogFile to empty * * Empty the contents of the $LogFile journal @na and return 0 on success and * -1 on error. * * This function assumes that the $LogFile journal has already been consistency * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean() * has been used to ensure that the $LogFile is clean. */int ntfs_empty_logfile(ntfs_attr *na, progress *prog, int progidx){ s64 len, pos, count; char *buf = NULL; int err; ntfs_log_trace("Entering.\n"); buf = malloc(NTFS_BUF_SIZE); if (buf == NULL) { err = ENOMEM; goto io_error_exit; } if (NVolLogFileEmpty(na->ni->vol)) goto done; /* The $DATA attribute of the $LogFile has to be non-resident. */ if (!NAttrNonResident(na)) { err = EIO; ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n"); goto io_error_exit; } /* Get length of $LogFile contents. */ len = na->data_size; if (!len) { ntfs_log_debug("$LogFile has zero length, no disk write " "needed.\n"); free(buf); return 0; } /* Read $LogFile until its end. We do this as a check for correct length thus making sure we are decompressing the mapping pairs array correctly and hence writing below is safe as well. */ pos = 0; while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0) { if (prog) progress_update(prog, progidx, pos, (len * 3)); pos += count; } if (count == -1 || pos != len) { err = errno; ntfs_log_debug("Amount of $LogFile data read does not " "correspond to expected length!\n"); if (count != -1) err = EIO; goto io_error_exit; } /* Fill the buffer with 0xff's. */ memset(buf, -1, NTFS_BUF_SIZE); /* Set the $DATA attribute. */ pos = 0; while ((count = len - pos) > 0) { if (count > NTFS_BUF_SIZE) count = NTFS_BUF_SIZE; if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { err = errno; ntfs_log_debug("Failed to set the $LogFile attribute " "value.\n"); if (count != -1) err = EIO; goto io_error_exit; } if (prog) progress_update(prog, progidx, (len + (pos * 2)), (len * 3)); pos += count; } /* Set the flag so we do not have to do it again on remount. */ NVolSetLogFileEmpty(na->ni->vol);done: ntfs_log_trace("Done.\n"); free(buf); return 0;io_error_exit: ntfs_attr_close(na); ntfs_inode_close(na->ni); errno = err; free(buf); return -1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -