"Fossies" - the Fresh Open Source Software Archive

Member "ntfs-3g_ntfsprogs-2017.3.23/libntfs-3g/logfile.c" (23 Mar 2017, 24448 Bytes) of package /linux/misc/ntfs-3g_ntfsprogs-2017.3.23.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "logfile.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3g_ntfsprogs-2016.2.22_vs_3g_ntfsprogs-2017.3.23.

    1 /**
    2  * logfile.c - NTFS journal handling. Originated from the Linux-NTFS project.
    3  *
    4  * Copyright (c) 2002-2005 Anton Altaparmakov
    5  * Copyright (c) 2005 Yura Pakhuchiy
    6  * Copyright (c) 2005-2009 Szabolcs Szakacsits
    7  *
    8  * This program/include file is free software; you can redistribute it and/or
    9  * modify it under the terms of the GNU General Public License as published
   10  * by the Free Software Foundation; either version 2 of the License, or
   11  * (at your option) any later version.
   12  *
   13  * This program/include file is distributed in the hope that it will be
   14  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
   15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program (in the main directory of the NTFS-3G
   20  * distribution in the file COPYING); if not, write to the Free Software
   21  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22  */
   23 
   24 #ifdef HAVE_CONFIG_H
   25 #include "config.h"
   26 #endif
   27 
   28 #ifdef HAVE_STDLIB_H
   29 #include <stdlib.h>
   30 #endif
   31 #ifdef HAVE_STRING_H
   32 #include <string.h>
   33 #endif
   34 #ifdef HAVE_ERRNO_H
   35 #include <errno.h>
   36 #endif
   37 
   38 #include "attrib.h"
   39 #include "debug.h"
   40 #include "logfile.h"
   41 #include "volume.h"
   42 #include "mst.h"
   43 #include "logging.h"
   44 #include "misc.h"
   45 
   46 /**
   47  * ntfs_check_restart_page_header - check the page header for consistency
   48  * @rp:     restart page header to check
   49  * @pos:    position in logfile at which the restart page header resides
   50  *
   51  * Check the restart page header @rp for consistency and return TRUE if it is
   52  * consistent and FALSE otherwise.
   53  *
   54  * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
   55  * require the full restart page.
   56  */
   57 static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
   58 {
   59     u32 logfile_system_page_size, logfile_log_page_size;
   60     u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
   61     BOOL have_usa = TRUE;
   62 
   63     ntfs_log_trace("Entering.\n");
   64     /*
   65      * If the system or log page sizes are smaller than the ntfs block size
   66      * or either is not a power of 2 we cannot handle this log file.
   67      */
   68     logfile_system_page_size = le32_to_cpu(rp->system_page_size);
   69     logfile_log_page_size = le32_to_cpu(rp->log_page_size);
   70     if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
   71             logfile_log_page_size < NTFS_BLOCK_SIZE ||
   72             logfile_system_page_size &
   73             (logfile_system_page_size - 1) ||
   74             logfile_log_page_size & (logfile_log_page_size - 1)) {
   75         ntfs_log_error("$LogFile uses unsupported page size.\n");
   76         return FALSE;
   77     }
   78     /*
   79      * We must be either at !pos (1st restart page) or at pos = system page
   80      * size (2nd restart page).
   81      */
   82     if (pos && pos != logfile_system_page_size) {
   83         ntfs_log_error("Found restart area in incorrect "
   84                 "position in $LogFile.\n");
   85         return FALSE;
   86     }
   87     /*
   88      * We only know how to handle version 1.1 and 2.0, though
   89      * version 2.0 is probably related to cached metadata in
   90      * Windows 8, and we will refuse to mount.
   91      * Nevertheless, do all the relevant checks before rejecting.
   92      */
   93     if (((rp->major_ver != const_cpu_to_sle16(1))
   94              || (rp->minor_ver != const_cpu_to_sle16(1)))
   95        && ((rp->major_ver != const_cpu_to_sle16(2))
   96              || (rp->minor_ver != const_cpu_to_sle16(0)))) {
   97         ntfs_log_error("$LogFile version %i.%i is not "
   98                 "supported.\n   (This driver supports version "
   99                 "1.1 and 2.0 only.)\n",
  100                     (int)sle16_to_cpu(rp->major_ver),
  101                     (int)sle16_to_cpu(rp->minor_ver));
  102         return FALSE;
  103     }
  104     /*
  105      * If chkdsk has been run the restart page may not be protected by an
  106      * update sequence array.
  107      */
  108     if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
  109         have_usa = FALSE;
  110         goto skip_usa_checks;
  111     }
  112     /* Verify the size of the update sequence array. */
  113     usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
  114     if (usa_count != le16_to_cpu(rp->usa_count)) {
  115         ntfs_log_error("$LogFile restart page specifies "
  116                 "inconsistent update sequence array count.\n");
  117         return FALSE;
  118     }
  119     /* Verify the position of the update sequence array. */
  120     usa_ofs = le16_to_cpu(rp->usa_ofs);
  121     usa_end = usa_ofs + usa_count * sizeof(u16);
  122     if (usa_ofs < offsetof(RESTART_PAGE_HEADER, usn) ||
  123             usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
  124         ntfs_log_error("$LogFile restart page specifies "
  125                 "inconsistent update sequence array offset.\n");
  126         return FALSE;
  127     }
  128 skip_usa_checks:
  129     /*
  130      * Verify the position of the restart area.  It must be:
  131      *  - aligned to 8-byte boundary,
  132      *  - after the update sequence array, and
  133      *  - within the system page size.
  134      */
  135     ra_ofs = le16_to_cpu(rp->restart_area_offset);
  136     if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
  137             ra_ofs < offsetof(RESTART_PAGE_HEADER, usn)) ||
  138             ra_ofs > logfile_system_page_size) {
  139         ntfs_log_error("$LogFile restart page specifies "
  140                 "inconsistent restart area offset.\n");
  141         return FALSE;
  142     }
  143     /*
  144      * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
  145      * set.
  146      */
  147     if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
  148         ntfs_log_error("$LogFile restart page is not modified "
  149                 "by chkdsk but a chkdsk LSN is specified.\n");
  150         return FALSE;
  151     }
  152     ntfs_log_trace("Done.\n");
  153     return TRUE;
  154 }
  155 
  156 /**
  157  * ntfs_check_restart_area - check the restart area for consistency
  158  * @rp:     restart page whose restart area to check
  159  *
  160  * Check the restart area of the restart page @rp for consistency and return
  161  * TRUE if it is consistent and FALSE otherwise.
  162  *
  163  * This function assumes that the restart page header has already been
  164  * consistency checked.
  165  *
  166  * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
  167  * require the full restart page.
  168  */
  169 static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
  170 {
  171     u64 file_size;
  172     RESTART_AREA *ra;
  173     u16 ra_ofs, ra_len, ca_ofs;
  174     u8 fs_bits;
  175 
  176     ntfs_log_trace("Entering.\n");
  177     ra_ofs = le16_to_cpu(rp->restart_area_offset);
  178     ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
  179     /*
  180      * Everything before ra->file_size must be before the first word
  181      * protected by an update sequence number.  This ensures that it is
  182      * safe to access ra->client_array_offset.
  183      */
  184     if (ra_ofs + offsetof(RESTART_AREA, file_size) >
  185             NTFS_BLOCK_SIZE - sizeof(u16)) {
  186         ntfs_log_error("$LogFile restart area specifies "
  187                 "inconsistent file offset.\n");
  188         return FALSE;
  189     }
  190     /*
  191      * Now that we can access ra->client_array_offset, make sure everything
  192      * up to the log client array is before the first word protected by an
  193      * update sequence number.  This ensures we can access all of the
  194      * restart area elements safely.  Also, the client array offset must be
  195      * aligned to an 8-byte boundary.
  196      */
  197     ca_ofs = le16_to_cpu(ra->client_array_offset);
  198     if (((ca_ofs + 7) & ~7) != ca_ofs ||
  199             ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
  200             sizeof(u16))) {
  201         ntfs_log_error("$LogFile restart area specifies "
  202                 "inconsistent client array offset.\n");
  203         return FALSE;
  204     }
  205     /*
  206      * The restart area must end within the system page size both when
  207      * calculated manually and as specified by ra->restart_area_length.
  208      * Also, the calculated length must not exceed the specified length.
  209      */
  210     ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
  211             sizeof(LOG_CLIENT_RECORD);
  212     if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
  213             (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
  214             le32_to_cpu(rp->system_page_size) ||
  215             ra_len > le16_to_cpu(ra->restart_area_length)) {
  216         ntfs_log_error("$LogFile restart area is out of bounds "
  217                 "of the system page size specified by the "
  218                 "restart page header and/or the specified "
  219                 "restart area length is inconsistent.\n");
  220         return FALSE;
  221     }
  222     /*
  223      * The ra->client_free_list and ra->client_in_use_list must be either
  224      * LOGFILE_NO_CLIENT or less than ra->log_clients or they are
  225      * overflowing the client array.
  226      */
  227     if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
  228             le16_to_cpu(ra->client_free_list) >=
  229             le16_to_cpu(ra->log_clients)) ||
  230             (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
  231             le16_to_cpu(ra->client_in_use_list) >=
  232             le16_to_cpu(ra->log_clients))) {
  233         ntfs_log_error("$LogFile restart area specifies "
  234                 "overflowing client free and/or in use lists.\n");
  235         return FALSE;
  236     }
  237     /*
  238      * Check ra->seq_number_bits against ra->file_size for consistency.
  239      * We cannot just use ffs() because the file size is not a power of 2.
  240      */
  241     file_size = (u64)sle64_to_cpu(ra->file_size);
  242     fs_bits = 0;
  243     while (file_size) {
  244         file_size >>= 1;
  245         fs_bits++;
  246     }
  247     if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
  248         ntfs_log_error("$LogFile restart area specifies "
  249                 "inconsistent sequence number bits.\n");
  250         return FALSE;
  251     }
  252     /* The log record header length must be a multiple of 8. */
  253     if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
  254             le16_to_cpu(ra->log_record_header_length)) {
  255         ntfs_log_error("$LogFile restart area specifies "
  256                 "inconsistent log record header length.\n");
  257         return FALSE;
  258     }
  259     /* Ditto for the log page data offset. */
  260     if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
  261             le16_to_cpu(ra->log_page_data_offset)) {
  262         ntfs_log_error("$LogFile restart area specifies "
  263                 "inconsistent log page data offset.\n");
  264         return FALSE;
  265     }
  266     ntfs_log_trace("Done.\n");
  267     return TRUE;
  268 }
  269 
  270 /**
  271  * ntfs_check_log_client_array - check the log client array for consistency
  272  * @rp:     restart page whose log client array to check
  273  *
  274  * Check the log client array of the restart page @rp for consistency and
  275  * return TRUE if it is consistent and FALSE otherwise.
  276  *
  277  * This function assumes that the restart page header and the restart area have
  278  * already been consistency checked.
  279  *
  280  * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
  281  * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
  282  * restart page and the page must be multi sector transfer deprotected.
  283  */
  284 static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
  285 {
  286     RESTART_AREA *ra;
  287     LOG_CLIENT_RECORD *ca, *cr;
  288     u16 nr_clients, idx;
  289     BOOL in_free_list, idx_is_first;
  290 
  291     ntfs_log_trace("Entering.\n");
  292     ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
  293     ca = (LOG_CLIENT_RECORD*)((u8*)ra +
  294             le16_to_cpu(ra->client_array_offset));
  295     /*
  296      * Check the ra->client_free_list first and then check the
  297      * ra->client_in_use_list.  Check each of the log client records in
  298      * each of the lists and check that the array does not overflow the
  299      * ra->log_clients value.  Also keep track of the number of records
  300      * visited as there cannot be more than ra->log_clients records and
  301      * that way we detect eventual loops in within a list.
  302      */
  303     nr_clients = le16_to_cpu(ra->log_clients);
  304     idx = le16_to_cpu(ra->client_free_list);
  305     in_free_list = TRUE;
  306 check_list:
  307     for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
  308             idx = le16_to_cpu(cr->next_client)) {
  309         if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
  310             goto err_out;
  311         /* Set @cr to the current log client record. */
  312         cr = ca + idx;
  313         /* The first log client record must not have a prev_client. */
  314         if (idx_is_first) {
  315             if (cr->prev_client != LOGFILE_NO_CLIENT)
  316                 goto err_out;
  317             idx_is_first = FALSE;
  318         }
  319     }
  320     /* Switch to and check the in use list if we just did the free list. */
  321     if (in_free_list) {
  322         in_free_list = FALSE;
  323         idx = le16_to_cpu(ra->client_in_use_list);
  324         goto check_list;
  325     }
  326     ntfs_log_trace("Done.\n");
  327     return TRUE;
  328 err_out:
  329     ntfs_log_error("$LogFile log client array is corrupt.\n");
  330     return FALSE;
  331 }
  332 
  333 /**
  334  * ntfs_check_and_load_restart_page - check the restart page for consistency
  335  * @log_na: opened ntfs attribute for journal $LogFile
  336  * @rp:     restart page to check
  337  * @pos:    position in @log_na at which the restart page resides
  338  * @wrp:       [OUT] copy of the multi sector transfer deprotected restart page
  339  * @lsn:       [OUT] set to the current logfile lsn on success
  340  *
  341  * Check the restart page @rp for consistency and return 0 if it is consistent
  342  * and errno otherwise.  The restart page may have been modified by chkdsk in
  343  * which case its magic is CHKD instead of RSTR.
  344  *
  345  * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
  346  * require the full restart page.
  347  *
  348  * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
  349  * copy of the complete multi sector transfer deprotected page.  On failure,
  350  * *@wrp is undefined.
  351  *
  352  * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
  353  * logfile lsn according to this restart page.  On failure, *@lsn is undefined.
  354  *
  355  * The following error codes are defined:
  356  *     EINVAL - The restart page is inconsistent.
  357  *     ENOMEM - Not enough memory to load the restart page.
  358  *     EIO    - Failed to reading from $LogFile.
  359  */
  360 static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
  361         RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
  362         LSN *lsn)
  363 {
  364     RESTART_AREA *ra;
  365     RESTART_PAGE_HEADER *trp;
  366     int err;
  367 
  368     ntfs_log_trace("Entering.\n");
  369     /* Check the restart page header for consistency. */
  370     if (!ntfs_check_restart_page_header(rp, pos)) {
  371         /* Error output already done inside the function. */
  372         return EINVAL;
  373     }
  374     /* Check the restart area for consistency. */
  375     if (!ntfs_check_restart_area(rp)) {
  376         /* Error output already done inside the function. */
  377         return EINVAL;
  378     }
  379     ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
  380     /*
  381      * Allocate a buffer to store the whole restart page so we can multi
  382      * sector transfer deprotect it.
  383      */
  384     trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
  385     if (!trp)
  386         return errno;
  387     /*
  388      * Read the whole of the restart page into the buffer.  If it fits
  389      * completely inside @rp, just copy it from there.  Otherwise read it
  390      * from disk.
  391      */
  392     if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
  393         memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
  394     else if (ntfs_attr_pread(log_na, pos,
  395             le32_to_cpu(rp->system_page_size), trp) !=
  396             le32_to_cpu(rp->system_page_size)) {
  397         err = errno;
  398         ntfs_log_error("Failed to read whole restart page into the "
  399                 "buffer.\n");
  400         if (err != ENOMEM)
  401             err = EIO;
  402         goto err_out;
  403     }
  404     /*
  405      * Perform the multi sector transfer deprotection on the buffer if the
  406      * restart page is protected.
  407      */
  408     if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
  409             && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
  410             le32_to_cpu(rp->system_page_size))) {
  411         /*
  412          * A multi sector tranfer error was detected.  We only need to
  413          * abort if the restart page contents exceed the multi sector
  414          * transfer fixup of the first sector.
  415          */
  416         if (le16_to_cpu(rp->restart_area_offset) +
  417                 le16_to_cpu(ra->restart_area_length) >
  418                 NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
  419             ntfs_log_error("Multi sector transfer error "
  420                    "detected in $LogFile restart page.\n");
  421             err = EINVAL;
  422             goto err_out;
  423         }
  424     }
  425     /*
  426      * If the restart page is modified by chkdsk or there are no active
  427      * logfile clients, the logfile is consistent.  Otherwise, need to
  428      * check the log client records for consistency, too.
  429      */
  430     err = 0;
  431     if (ntfs_is_rstr_record(rp->magic) &&
  432             ra->client_in_use_list != LOGFILE_NO_CLIENT) {
  433         if (!ntfs_check_log_client_array(trp)) {
  434             err = EINVAL;
  435             goto err_out;
  436         }
  437     }
  438     if (lsn) {
  439         if (ntfs_is_rstr_record(rp->magic))
  440             *lsn = sle64_to_cpu(ra->current_lsn);
  441         else /* if (ntfs_is_chkd_record(rp->magic)) */
  442             *lsn = sle64_to_cpu(rp->chkdsk_lsn);
  443     }
  444     ntfs_log_trace("Done.\n");
  445     if (wrp)
  446         *wrp = trp;
  447     else {
  448 err_out:
  449         free(trp);
  450     }
  451     return err;
  452 }
  453 
  454 /**
  455  * ntfs_check_logfile - check in the journal if the volume is consistent
  456  * @log_na: ntfs attribute of loaded journal $LogFile to check
  457  * @rp:         [OUT] on success this is a copy of the current restart page
  458  *
  459  * Check the $LogFile journal for consistency and return TRUE if it is
  460  * consistent and FALSE if not.  On success, the current restart page is
  461  * returned in *@rp.  Caller must call ntfs_free(*@rp) when finished with it.
  462  *
  463  * At present we only check the two restart pages and ignore the log record
  464  * pages.
  465  *
  466  * Note that the MstProtected flag is not set on the $LogFile inode and hence
  467  * when reading pages they are not deprotected.  This is because we do not know
  468  * if the $LogFile was created on a system with a different page size to ours
  469  * yet and mst deprotection would fail if our page size is smaller.
  470  */
  471 BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
  472 {
  473     s64 size, pos;
  474     LSN rstr1_lsn, rstr2_lsn;
  475     ntfs_volume *vol = log_na->ni->vol;
  476     u8 *kaddr = NULL;
  477     RESTART_PAGE_HEADER *rstr1_ph = NULL;
  478     RESTART_PAGE_HEADER *rstr2_ph = NULL;
  479     int log_page_size, err;
  480     BOOL logfile_is_empty = TRUE;
  481     u8 log_page_bits;
  482 
  483     ntfs_log_trace("Entering.\n");
  484     /* An empty $LogFile must have been clean before it got emptied. */
  485     if (NVolLogFileEmpty(vol))
  486         goto is_empty;
  487     size = log_na->data_size;
  488     /* Make sure the file doesn't exceed the maximum allowed size. */
  489     if (size > (s64)MaxLogFileSize)
  490         size = MaxLogFileSize;
  491     log_page_size = DefaultLogPageSize;
  492     /*
  493      * Use generic_ffs() instead of ffs() to enable the compiler to
  494      * optimize log_page_size and log_page_bits into constants.
  495      */
  496     log_page_bits = ffs(log_page_size) - 1;
  497     size &= ~(log_page_size - 1);
  498 
  499     /*
  500      * Ensure the log file is big enough to store at least the two restart
  501      * pages and the minimum number of log record pages.
  502      */
  503     if (size < log_page_size * 2 || (size - log_page_size * 2) >>
  504             log_page_bits < MinLogRecordPages) {
  505         ntfs_log_error("$LogFile is too small.\n");
  506         return FALSE;
  507     }
  508     /* Allocate memory for restart page. */
  509     kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
  510     if (!kaddr)
  511         return FALSE;
  512     /*
  513      * Read through the file looking for a restart page.  Since the restart
  514      * page header is at the beginning of a page we only need to search at
  515      * what could be the beginning of a page (for each page size) rather
  516      * than scanning the whole file byte by byte.  If all potential places
  517      * contain empty and uninitialized records, the log file can be assumed
  518      * to be empty.
  519      */
  520     for (pos = 0; pos < size; pos <<= 1) {
  521         /*
  522          * Read first NTFS_BLOCK_SIZE bytes of potential restart page.
  523          */
  524         if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
  525                 NTFS_BLOCK_SIZE) {
  526             ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
  527                     "bytes of potential restart page.\n");
  528             goto err_out;
  529         }
  530 
  531         /*
  532          * A non-empty block means the logfile is not empty while an
  533          * empty block after a non-empty block has been encountered
  534          * means we are done.
  535          */
  536         if (!ntfs_is_empty_recordp((le32*)kaddr))
  537             logfile_is_empty = FALSE;
  538         else if (!logfile_is_empty)
  539             break;
  540         /*
  541          * A log record page means there cannot be a restart page after
  542          * this so no need to continue searching.
  543          */
  544         if (ntfs_is_rcrd_recordp((le32*)kaddr))
  545             break;
  546         /* If not a (modified by chkdsk) restart page, continue. */
  547         if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
  548                 !ntfs_is_chkd_recordp((le32*)kaddr)) {
  549             if (!pos)
  550                 pos = NTFS_BLOCK_SIZE >> 1;
  551             continue;
  552         }
  553         /*
  554          * Check the (modified by chkdsk) restart page for consistency
  555          * and get a copy of the complete multi sector transfer
  556          * deprotected restart page.
  557          */
  558         err = ntfs_check_and_load_restart_page(log_na,
  559                 (RESTART_PAGE_HEADER*)kaddr, pos,
  560                 !rstr1_ph ? &rstr1_ph : &rstr2_ph,
  561                 !rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
  562         if (!err) {
  563             /*
  564              * If we have now found the first (modified by chkdsk)
  565              * restart page, continue looking for the second one.
  566              */
  567             if (!pos) {
  568                 pos = NTFS_BLOCK_SIZE >> 1;
  569                 continue;
  570             }
  571             /*
  572              * We have now found the second (modified by chkdsk)
  573              * restart page, so we can stop looking.
  574              */
  575             break;
  576         }
  577         /*
  578          * Error output already done inside the function.  Note, we do
  579          * not abort if the restart page was invalid as we might still
  580          * find a valid one further in the file.
  581          */
  582         if (err != EINVAL)
  583               goto err_out;
  584         /* Continue looking. */
  585         if (!pos)
  586             pos = NTFS_BLOCK_SIZE >> 1;
  587     }
  588     if (kaddr) {
  589         free(kaddr);
  590         kaddr = NULL;
  591     }
  592     if (logfile_is_empty) {
  593         NVolSetLogFileEmpty(vol);
  594 is_empty:
  595         ntfs_log_trace("Done.  ($LogFile is empty.)\n");
  596         return TRUE;
  597     }
  598     if (!rstr1_ph) {
  599         if (rstr2_ph)
  600             ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
  601         ntfs_log_error("Did not find any restart pages in "
  602                "$LogFile and it was not empty.\n");
  603         return FALSE;
  604     }
  605     /* If both restart pages were found, use the more recent one. */
  606     if (rstr2_ph) {
  607         /*
  608          * If the second restart area is more recent, switch to it.
  609          * Otherwise just throw it away.
  610          */
  611         if (rstr2_lsn > rstr1_lsn) {
  612             ntfs_log_debug("Using second restart page as it is more "
  613                     "recent.\n");
  614             free(rstr1_ph);
  615             rstr1_ph = rstr2_ph;
  616             /* rstr1_lsn = rstr2_lsn; */
  617         } else {
  618             ntfs_log_debug("Using first restart page as it is more "
  619                     "recent.\n");
  620             free(rstr2_ph);
  621         }
  622         rstr2_ph = NULL;
  623     }
  624     /* All consistency checks passed. */
  625     if (rp)
  626         *rp = rstr1_ph;
  627     else
  628         free(rstr1_ph);
  629     ntfs_log_trace("Done.\n");
  630     return TRUE;
  631 err_out:
  632     free(kaddr);
  633     free(rstr1_ph);
  634     free(rstr2_ph);
  635     return FALSE;
  636 }
  637 
  638 /**
  639  * ntfs_is_logfile_clean - check in the journal if the volume is clean
  640  * @log_na: ntfs attribute of loaded journal $LogFile to check
  641  * @rp:         copy of the current restart page
  642  *
  643  * Analyze the $LogFile journal and return TRUE if it indicates the volume was
  644  * shutdown cleanly and FALSE if not.
  645  *
  646  * At present we only look at the two restart pages and ignore the log record
  647  * pages.  This is a little bit crude in that there will be a very small number
  648  * of cases where we think that a volume is dirty when in fact it is clean.
  649  * This should only affect volumes that have not been shutdown cleanly but did
  650  * not have any pending, non-check-pointed i/o, i.e. they were completely idle
  651  * at least for the five seconds preceding the unclean shutdown.
  652  *
  653  * This function assumes that the $LogFile journal has already been consistency
  654  * checked by a call to ntfs_check_logfile() and in particular if the $LogFile
  655  * is empty this function requires that NVolLogFileEmpty() is true otherwise an
  656  * empty volume will be reported as dirty.
  657  */
  658 BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
  659 {
  660     RESTART_AREA *ra;
  661 
  662     ntfs_log_trace("Entering.\n");
  663     /* An empty $LogFile must have been clean before it got emptied. */
  664     if (NVolLogFileEmpty(log_na->ni->vol)) {
  665         ntfs_log_trace("$LogFile is empty\n");
  666         return TRUE;
  667     }
  668     if (!rp) {
  669         ntfs_log_error("Restart page header is NULL\n");
  670         return FALSE;
  671     }
  672     if (!ntfs_is_rstr_record(rp->magic) &&
  673             !ntfs_is_chkd_record(rp->magic)) {
  674         ntfs_log_error("Restart page buffer is invalid\n");
  675         return FALSE;
  676     }
  677 
  678     ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
  679     /*
  680      * If the $LogFile has active clients, i.e. it is open, and we do not
  681      * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
  682      * we assume there was an unclean shutdown.
  683      */
  684     if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
  685             !(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
  686         ntfs_log_error("The disk contains an unclean file system (%d, "
  687                    "%d).\n", le16_to_cpu(ra->client_in_use_list),
  688                    le16_to_cpu(ra->flags));
  689         return FALSE;
  690     }
  691     /* $LogFile indicates a clean shutdown. */
  692     ntfs_log_trace("$LogFile indicates a clean shutdown\n");
  693     return TRUE;
  694 }
  695 
  696 /**
  697  * ntfs_empty_logfile - empty the contents of the $LogFile journal
  698  * @na:     ntfs attribute of journal $LogFile to empty
  699  *
  700  * Empty the contents of the $LogFile journal @na and return 0 on success and
  701  * -1 on error.
  702  *
  703  * This function assumes that the $LogFile journal has already been consistency
  704  * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
  705  * has been used to ensure that the $LogFile is clean.
  706  */
  707 int ntfs_empty_logfile(ntfs_attr *na)
  708 {
  709     s64 pos, count;
  710     char buf[NTFS_BUF_SIZE];
  711 
  712     ntfs_log_trace("Entering.\n");
  713     
  714     if (NVolLogFileEmpty(na->ni->vol))
  715         return 0;
  716 
  717     if (!NAttrNonResident(na)) {
  718         errno = EIO;
  719         ntfs_log_perror("Resident $LogFile $DATA attribute");
  720         return -1;
  721     }
  722 
  723     memset(buf, -1, NTFS_BUF_SIZE);
  724 
  725     pos = 0;
  726     while ((count = na->data_size - pos) > 0) {
  727         
  728         if (count > NTFS_BUF_SIZE)
  729             count = NTFS_BUF_SIZE;
  730 
  731         count = ntfs_attr_pwrite(na, pos, count, buf);
  732         if (count <= 0) {
  733             ntfs_log_perror("Failed to reset $LogFile");
  734             if (count != -1)
  735                 errno = EIO;
  736             return -1;
  737         }
  738         pos += count;
  739     }
  740 
  741     NVolSetLogFileEmpty(na->ni->vol);
  742     
  743     return 0;
  744 }