NTFS: Support more clean journal ($LogFile) states.

- Support journals ($LogFile) which have been modified by chkdsk.  This
        means users can boot into Windows after we marked the volume dirty.
        The Windows boot will run chkdsk and then reboot.  The user can then
        immediately boot into Linux rather than having to do a full Windows
        boot first before rebooting into Linux and we will recognize such a
        journal and empty it as it is clean by definition.
      - Support journals ($LogFile) with only one restart page as well as
        journals with two different restart pages.  We sanity check both and
        either use the only sane one or the more recent one of the two in the
        case that both are valid.

Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
This commit is contained in:
Anton Altaparmakov 2005-09-08 16:12:28 +01:00
parent caf39e87cc
commit e7a1033b94
5 changed files with 166 additions and 122 deletions

View file

@ -22,6 +22,19 @@ ToDo/Notes:
- Enable the code for setting the NT4 compatibility flag when we start - Enable the code for setting the NT4 compatibility flag when we start
making NTFS 1.2 specific modifications. making NTFS 1.2 specific modifications.
2.1.24-WIP
- Support journals ($LogFile) which have been modified by chkdsk. This
means users can boot into Windows after we marked the volume dirty.
The Windows boot will run chkdsk and then reboot. The user can then
immediately boot into Linux rather than having to do a full Windows
boot first before rebooting into Linux and we will recognize such a
journal and empty it as it is clean by definition.
- Support journals ($LogFile) with only one restart page as well as
journals with two different restart pages. We sanity check both and
either use the only sane one or the more recent one of the two in the
case that both are valid.
2.1.23 - Implement extension of resident files and make writing safe as well as 2.1.23 - Implement extension of resident files and make writing safe as well as
many bug fixes, cleanups, and enhancements... many bug fixes, cleanups, and enhancements...

View file

@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
unistr.o upcase.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.23\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24-WIP\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG

View file

@ -121,7 +121,7 @@ static BOOL ntfs_check_restart_page_header(struct inode *vi,
*/ */
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) { if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
ntfs_error(vi->i_sb, "$LogFile restart page is not modified " ntfs_error(vi->i_sb, "$LogFile restart page is not modified "
"chkdsk but a chkdsk LSN is specified."); "by chkdsk but a chkdsk LSN is specified.");
return FALSE; return FALSE;
} }
ntfs_debug("Done."); ntfs_debug("Done.");
@ -312,10 +312,12 @@ err_out:
* @vi: $LogFile inode to which the restart page belongs * @vi: $LogFile inode to which the restart page belongs
* @rp: restart page to check * @rp: restart page to check
* @pos: position in @vi at which the restart page resides * @pos: position in @vi at which the restart page resides
* @wrp: copy of the multi sector transfer deprotected restart page * @wrp: [OUT] copy of the multi sector transfer deprotected restart page
* @lsn: [OUT] set to the current logfile lsn on success
* *
* Check the restart page @rp for consistency and return TRUE if it is * Check the restart page @rp for consistency and return 0 if it is consistent
* consistent and FALSE otherwise. * and -errno otherwise. The restart page may have been modified by chkdsk in
* which case its magic is CHKD instead of RSTR.
* *
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
* require the full restart page. * require the full restart page.
@ -323,25 +325,33 @@ err_out:
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
* copy of the complete multi sector transfer deprotected page. On failure, * copy of the complete multi sector transfer deprotected page. On failure,
* *@wrp is undefined. * *@wrp is undefined.
*
* Simillarly, if @lsn is not NULL, on succes *@lsn will be set to the current
* logfile lsn according to this restart page. On failure, *@lsn is undefined.
*
* The following error codes are defined:
* -EINVAL - The restart page is inconsistent.
* -ENOMEM - Not enough memory to load the restart page.
* -EIO - Failed to reading from $LogFile.
*/ */
static BOOL ntfs_check_and_load_restart_page(struct inode *vi, static int ntfs_check_and_load_restart_page(struct inode *vi,
RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp) RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
LSN *lsn)
{ {
RESTART_AREA *ra; RESTART_AREA *ra;
RESTART_PAGE_HEADER *trp; RESTART_PAGE_HEADER *trp;
int size; int size, err;
BOOL ret;
ntfs_debug("Entering."); ntfs_debug("Entering.");
/* Check the restart page header for consistency. */ /* Check the restart page header for consistency. */
if (!ntfs_check_restart_page_header(vi, rp, pos)) { if (!ntfs_check_restart_page_header(vi, rp, pos)) {
/* Error output already done inside the function. */ /* Error output already done inside the function. */
return FALSE; return -EINVAL;
} }
/* Check the restart area for consistency. */ /* Check the restart area for consistency. */
if (!ntfs_check_restart_area(vi, rp)) { if (!ntfs_check_restart_area(vi, rp)) {
/* Error output already done inside the function. */ /* Error output already done inside the function. */
return FALSE; return -EINVAL;
} }
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
/* /*
@ -352,7 +362,7 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi,
if (!trp) { if (!trp) {
ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile " ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile "
"restart page buffer."); "restart page buffer.");
return FALSE; return -ENOMEM;
} }
/* /*
* Read the whole of the restart page into the buffer. If it fits * Read the whole of the restart page into the buffer. If it fits
@ -379,6 +389,9 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi,
if (IS_ERR(page)) { if (IS_ERR(page)) {
ntfs_error(vi->i_sb, "Error mapping $LogFile " ntfs_error(vi->i_sb, "Error mapping $LogFile "
"page (index %lu).", idx); "page (index %lu).", idx);
err = PTR_ERR(page);
if (err != -EIO && err != -ENOMEM)
err = -EIO;
goto err_out; goto err_out;
} }
size = min_t(int, to_read, PAGE_CACHE_SIZE); size = min_t(int, to_read, PAGE_CACHE_SIZE);
@ -392,29 +405,57 @@ static BOOL ntfs_check_and_load_restart_page(struct inode *vi,
/* Perform the multi sector transfer deprotection on the buffer. */ /* Perform the multi sector transfer deprotection on the buffer. */
if (post_read_mst_fixup((NTFS_RECORD*)trp, if (post_read_mst_fixup((NTFS_RECORD*)trp,
le32_to_cpu(rp->system_page_size))) { le32_to_cpu(rp->system_page_size))) {
ntfs_error(vi->i_sb, "Multi sector transfer error detected in " /*
"$LogFile restart page."); * A multi sector tranfer error was detected. We only need to
goto err_out; * 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 - sizeof(u16)) {
ntfs_error(vi->i_sb, "Multi sector transfer error "
"detected in $LogFile restart page.");
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(vi, 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);
} }
/* Check the log client records for consistency. */
ret = ntfs_check_log_client_array(vi, trp);
if (ret && wrp)
*wrp = trp;
else
ntfs_free(trp);
ntfs_debug("Done."); ntfs_debug("Done.");
return ret; if (wrp)
*wrp = trp;
else {
err_out: err_out:
ntfs_free(trp); ntfs_free(trp);
return FALSE; }
return err;
} }
/** /**
* ntfs_check_logfile - check the journal for consistency * ntfs_check_logfile - check the journal for consistency
* @log_vi: struct inode of loaded journal $LogFile to check * @log_vi: struct inode 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 * Check the $LogFile journal for consistency and return TRUE if it is
* consistent and FALSE if not. * 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 * At present we only check the two restart pages and ignore the log record
* pages. * pages.
@ -424,19 +465,18 @@ err_out:
* if the $LogFile was created on a system with a different page size to ours * 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. * yet and mst deprotection would fail if our page size is smaller.
*/ */
BOOL ntfs_check_logfile(struct inode *log_vi) BOOL ntfs_check_logfile(struct inode *log_vi, RESTART_PAGE_HEADER **rp)
{ {
s64 size, pos, rstr1_pos, rstr2_pos; s64 size, pos;
LSN rstr1_lsn, rstr2_lsn;
ntfs_volume *vol = NTFS_SB(log_vi->i_sb); ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
struct address_space *mapping = log_vi->i_mapping; struct address_space *mapping = log_vi->i_mapping;
struct page *page = NULL; struct page *page = NULL;
u8 *kaddr = NULL; u8 *kaddr = NULL;
RESTART_PAGE_HEADER *rstr1_ph = NULL; RESTART_PAGE_HEADER *rstr1_ph = NULL;
RESTART_PAGE_HEADER *rstr2_ph = NULL; RESTART_PAGE_HEADER *rstr2_ph = NULL;
int log_page_size, log_page_mask, ofs; int log_page_size, log_page_mask, err;
BOOL logfile_is_empty = TRUE; BOOL logfile_is_empty = TRUE;
BOOL rstr1_found = FALSE;
BOOL rstr2_found = FALSE;
u8 log_page_bits; u8 log_page_bits;
ntfs_debug("Entering."); ntfs_debug("Entering.");
@ -491,7 +531,7 @@ BOOL ntfs_check_logfile(struct inode *log_vi)
if (IS_ERR(page)) { if (IS_ERR(page)) {
ntfs_error(vol->sb, "Error mapping $LogFile " ntfs_error(vol->sb, "Error mapping $LogFile "
"page (index %lu).", idx); "page (index %lu).", idx);
return FALSE; goto err_out;
} }
} }
kaddr = (u8*)page_address(page) + (pos & ~PAGE_CACHE_MASK); kaddr = (u8*)page_address(page) + (pos & ~PAGE_CACHE_MASK);
@ -510,99 +550,95 @@ BOOL ntfs_check_logfile(struct inode *log_vi)
*/ */
if (ntfs_is_rcrd_recordp((le32*)kaddr)) if (ntfs_is_rcrd_recordp((le32*)kaddr))
break; break;
/* /* If not a (modified by chkdsk) restart page, continue. */
* A modified by chkdsk restart page means we cannot handle if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
* this log file. !ntfs_is_chkd_recordp((le32*)kaddr)) {
*/
if (ntfs_is_chkd_recordp((le32*)kaddr)) {
ntfs_error(vol->sb, "$LogFile has been modified by "
"chkdsk. Mount this volume in "
"Windows.");
goto err_out;
}
/* If not a restart page, continue. */
if (!ntfs_is_rstr_recordp((le32*)kaddr)) {
/* Skip to the minimum page size for the next one. */
if (!pos) if (!pos)
pos = NTFS_BLOCK_SIZE >> 1; pos = NTFS_BLOCK_SIZE >> 1;
continue; continue;
} }
/* We now know we have a restart page. */ /*
if (!pos) { * Check the (modified by chkdsk) restart page for consistency
rstr1_found = TRUE; * and get a copy of the complete multi sector transfer
rstr1_pos = pos; * deprotected restart page.
} else { */
if (rstr2_found) { err = ntfs_check_and_load_restart_page(log_vi,
ntfs_error(vol->sb, "Found more than two " (RESTART_PAGE_HEADER*)kaddr, pos,
"restart pages in $LogFile."); !rstr1_ph ? &rstr1_ph : &rstr2_ph,
goto err_out; !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;
} }
rstr2_found = TRUE; /*
rstr2_pos = pos; * We have now found the second (modified by chkdsk)
* restart page, so we can stop looking.
*/
break;
} }
/* /*
* Check the restart page for consistency and get a copy of the * Error output already done inside the function. Note, we do
* complete multi sector transfer deprotected restart page. * not abort if the restart page was invalid as we might still
* find a valid one further in the file.
*/ */
if (!ntfs_check_and_load_restart_page(log_vi, if (err != -EINVAL) {
(RESTART_PAGE_HEADER*)kaddr, pos, ntfs_unmap_page(page);
!pos ? &rstr1_ph : &rstr2_ph)) {
/* Error output already done inside the function. */
goto err_out; goto err_out;
} }
/* /* Continue looking. */
* We have a valid restart page. The next one must be after
* a whole system page size as specified by the valid restart
* page.
*/
if (!pos) if (!pos)
pos = le32_to_cpu(rstr1_ph->system_page_size) >> 1; pos = NTFS_BLOCK_SIZE >> 1;
} }
if (page) { if (page)
ntfs_unmap_page(page); ntfs_unmap_page(page);
page = NULL;
}
if (logfile_is_empty) { if (logfile_is_empty) {
NVolSetLogFileEmpty(vol); NVolSetLogFileEmpty(vol);
is_empty: is_empty:
ntfs_debug("Done. ($LogFile is empty.)"); ntfs_debug("Done. ($LogFile is empty.)");
return TRUE; return TRUE;
} }
if (!rstr1_found || !rstr2_found) { if (!rstr1_ph) {
ntfs_error(vol->sb, "Did not find two restart pages in " BUG_ON(rstr2_ph);
"$LogFile."); ntfs_error(vol->sb, "Did not find any restart pages in "
goto err_out; "$LogFile and it was not empty.");
return FALSE;
} }
/* /* If both restart pages were found, use the more recent one. */
* The two restart areas must be identical except for the update if (rstr2_ph) {
* sequence number. /*
*/ * If the second restart area is more recent, switch to it.
ofs = le16_to_cpu(rstr1_ph->usa_ofs); * Otherwise just throw it away.
if (memcmp(rstr1_ph, rstr2_ph, ofs) || (ofs += sizeof(u16), */
memcmp((u8*)rstr1_ph + ofs, (u8*)rstr2_ph + ofs, if (rstr2_lsn > rstr1_lsn) {
le32_to_cpu(rstr1_ph->system_page_size) - ofs))) { ntfs_free(rstr1_ph);
ntfs_error(vol->sb, "The two restart pages in $LogFile do not " rstr1_ph = rstr2_ph;
"match."); /* rstr1_lsn = rstr2_lsn; */
goto err_out; } else
ntfs_free(rstr2_ph);
rstr2_ph = NULL;
} }
ntfs_free(rstr1_ph);
ntfs_free(rstr2_ph);
/* All consistency checks passed. */ /* All consistency checks passed. */
if (rp)
*rp = rstr1_ph;
else
ntfs_free(rstr1_ph);
ntfs_debug("Done."); ntfs_debug("Done.");
return TRUE; return TRUE;
err_out: err_out:
if (page)
ntfs_unmap_page(page);
if (rstr1_ph) if (rstr1_ph)
ntfs_free(rstr1_ph); ntfs_free(rstr1_ph);
if (rstr2_ph)
ntfs_free(rstr2_ph);
return FALSE; return FALSE;
} }
/** /**
* ntfs_is_logfile_clean - check in the journal if the volume is clean * ntfs_is_logfile_clean - check in the journal if the volume is clean
* @log_vi: struct inode of loaded journal $LogFile to check * @log_vi: struct inode 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 * Analyze the $LogFile journal and return TRUE if it indicates the volume was
* shutdown cleanly and FALSE if not. * shutdown cleanly and FALSE if not.
@ -619,11 +655,9 @@ err_out:
* is empty this function requires that NVolLogFileEmpty() is true otherwise an * is empty this function requires that NVolLogFileEmpty() is true otherwise an
* empty volume will be reported as dirty. * empty volume will be reported as dirty.
*/ */
BOOL ntfs_is_logfile_clean(struct inode *log_vi) BOOL ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER *rp)
{ {
ntfs_volume *vol = NTFS_SB(log_vi->i_sb); ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
struct page *page;
RESTART_PAGE_HEADER *rp;
RESTART_AREA *ra; RESTART_AREA *ra;
ntfs_debug("Entering."); ntfs_debug("Entering.");
@ -632,24 +666,15 @@ BOOL ntfs_is_logfile_clean(struct inode *log_vi)
ntfs_debug("Done. ($LogFile is empty.)"); ntfs_debug("Done. ($LogFile is empty.)");
return TRUE; return TRUE;
} }
/* BUG_ON(!rp);
* Read the first restart page. It will be possibly incomplete and if (!ntfs_is_rstr_record(rp->magic) &&
* will not be multi sector transfer deprotected but we only need the !ntfs_is_chkd_record(rp->magic)) {
* first NTFS_BLOCK_SIZE bytes so it does not matter. ntfs_error(vol->sb, "Restart page buffer is invalid. This is "
*/ "probably a bug in that the $LogFile should "
page = ntfs_map_page(log_vi->i_mapping, 0); "have been consistency checked before calling "
if (IS_ERR(page)) { "this function.");
ntfs_error(vol->sb, "Error mapping $LogFile page (index 0).");
return FALSE; return FALSE;
} }
rp = (RESTART_PAGE_HEADER*)page_address(page);
if (!ntfs_is_rstr_record(rp->magic)) {
ntfs_error(vol->sb, "No restart page found at offset zero in "
"$LogFile. This is probably a bug in that "
"the $LogFile should have been consistency "
"checked before calling this function.");
goto err_out;
}
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset)); 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 * If the $LogFile has active clients, i.e. it is open, and we do not
@ -659,15 +684,11 @@ BOOL ntfs_is_logfile_clean(struct inode *log_vi)
if (ra->client_in_use_list != LOGFILE_NO_CLIENT && if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
!(ra->flags & RESTART_VOLUME_IS_CLEAN)) { !(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
ntfs_debug("Done. $LogFile indicates a dirty shutdown."); ntfs_debug("Done. $LogFile indicates a dirty shutdown.");
goto err_out; return FALSE;
} }
ntfs_unmap_page(page);
/* $LogFile indicates a clean shutdown. */ /* $LogFile indicates a clean shutdown. */
ntfs_debug("Done. $LogFile indicates a clean shutdown."); ntfs_debug("Done. $LogFile indicates a clean shutdown.");
return TRUE; return TRUE;
err_out:
ntfs_unmap_page(page);
return FALSE;
} }
/** /**

View file

@ -2,7 +2,7 @@
* logfile.h - Defines for NTFS kernel journal ($LogFile) handling. Part of * logfile.h - Defines for NTFS kernel journal ($LogFile) handling. Part of
* the Linux-NTFS project. * the Linux-NTFS project.
* *
* Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2000-2005 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
@ -296,9 +296,11 @@ typedef struct {
/* sizeof() = 160 (0xa0) bytes */ /* sizeof() = 160 (0xa0) bytes */
} __attribute__ ((__packed__)) LOG_CLIENT_RECORD; } __attribute__ ((__packed__)) LOG_CLIENT_RECORD;
extern BOOL ntfs_check_logfile(struct inode *log_vi); extern BOOL ntfs_check_logfile(struct inode *log_vi,
RESTART_PAGE_HEADER **rp);
extern BOOL ntfs_is_logfile_clean(struct inode *log_vi); extern BOOL ntfs_is_logfile_clean(struct inode *log_vi,
const RESTART_PAGE_HEADER *rp);
extern BOOL ntfs_empty_logfile(struct inode *log_vi); extern BOOL ntfs_empty_logfile(struct inode *log_vi);

View file

@ -1133,7 +1133,8 @@ mft_unmap_out:
* *
* Return TRUE on success or FALSE on error. * Return TRUE on success or FALSE on error.
*/ */
static BOOL load_and_check_logfile(ntfs_volume *vol) static BOOL load_and_check_logfile(ntfs_volume *vol,
RESTART_PAGE_HEADER **rp)
{ {
struct inode *tmp_ino; struct inode *tmp_ino;
@ -1145,7 +1146,7 @@ static BOOL load_and_check_logfile(ntfs_volume *vol)
/* Caller will display error message. */ /* Caller will display error message. */
return FALSE; return FALSE;
} }
if (!ntfs_check_logfile(tmp_ino)) { if (!ntfs_check_logfile(tmp_ino, rp)) {
iput(tmp_ino); iput(tmp_ino);
/* ntfs_check_logfile() will have displayed error output. */ /* ntfs_check_logfile() will have displayed error output. */
return FALSE; return FALSE;
@ -1687,6 +1688,7 @@ static BOOL load_system_files(ntfs_volume *vol)
struct super_block *sb = vol->sb; struct super_block *sb = vol->sb;
MFT_RECORD *m; MFT_RECORD *m;
VOLUME_INFORMATION *vi; VOLUME_INFORMATION *vi;
RESTART_PAGE_HEADER *rp;
ntfs_attr_search_ctx *ctx; ntfs_attr_search_ctx *ctx;
#ifdef NTFS_RW #ifdef NTFS_RW
int err; int err;
@ -1841,8 +1843,9 @@ get_ctx_vol_failed:
* Get the inode for the logfile, check it and determine if the volume * Get the inode for the logfile, check it and determine if the volume
* was shutdown cleanly. * was shutdown cleanly.
*/ */
if (!load_and_check_logfile(vol) || rp = NULL;
!ntfs_is_logfile_clean(vol->logfile_ino)) { if (!load_and_check_logfile(vol, &rp) ||
!ntfs_is_logfile_clean(vol->logfile_ino, rp)) {
static const char *es1a = "Failed to load $LogFile"; static const char *es1a = "Failed to load $LogFile";
static const char *es1b = "$LogFile is not clean"; static const char *es1b = "$LogFile is not clean";
static const char *es2 = ". Mount in Windows."; static const char *es2 = ". Mount in Windows.";
@ -1857,6 +1860,10 @@ get_ctx_vol_failed:
"continue nor on_errors=" "continue nor on_errors="
"remount-ro was specified%s", "remount-ro was specified%s",
es1, es2); es1, es2);
if (vol->logfile_ino) {
BUG_ON(!rp);
ntfs_free(rp);
}
goto iput_logfile_err_out; goto iput_logfile_err_out;
} }
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
@ -1867,6 +1874,7 @@ get_ctx_vol_failed:
/* This will prevent a read-write remount. */ /* This will prevent a read-write remount. */
NVolSetErrors(vol); NVolSetErrors(vol);
} }
ntfs_free(rp);
#endif /* NTFS_RW */ #endif /* NTFS_RW */
/* Get the root directory inode so we can do path lookups. */ /* Get the root directory inode so we can do path lookups. */
vol->root_ino = ntfs_iget(sb, FILE_root); vol->root_ino = ntfs_iget(sb, FILE_root);