Commit 589ab626 authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: Read the journal ($LogFile) and determine if the volume has been shutdown cleanly

      and force a read-only mount if not (fs/ntfs/super.c and fs/ntfs/logfile.c).  This
      is a little bit of a crude check in that we only look at the restart areas and
      not at the actual log records so 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 and did not have any pending,
      non-check-pointed i/o.
parent ffa625ad
...@@ -38,11 +38,20 @@ ToDo: ...@@ -38,11 +38,20 @@ ToDo:
- Determine the mft mirror size as the number of mirrored mft records - Determine the mft mirror size as the number of mirrored mft records
and store it in ntfs_volume->mftmirr_size (fs/ntfs/super.c). and store it in ntfs_volume->mftmirr_size (fs/ntfs/super.c).
- Load the mft mirror at mount time and compare the mft records stored - Load the mft mirror at mount time and compare the mft records stored
in it to the ones in the mft (fs/ntfs/super.c). in it to the ones in the mft. Force a read-only mount if the two do
not match (fs/ntfs/super.c).
- Fix type casting related warnings on 64-bit architectures. Thanks - Fix type casting related warnings on 64-bit architectures. Thanks
to Meelis Roos for reporting them. to Meelis Roos for reporting them.
- Move %L to %ll as %L is floating point and %ll is integer which is - Move %L to %ll as %L is floating point and %ll is integer which is
what we want. what we want.
- Read the journal ($LogFile) and determine if the volume has been
shutdown cleanly and force a read-only mount if not (fs/ntfs/super.c
and fs/ntfs/logfile.c). This is a little bit of a crude check in
that we only look at the restart areas and not at the actual log
records so 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 and did not
have any pending, non-check-pointed i/o.
2.1.7 - Enable NFS exporting of mounted NTFS volumes. 2.1.7 - Enable NFS exporting of mounted NTFS volumes.
......
...@@ -2,15 +2,15 @@ ...@@ -2,15 +2,15 @@
obj-$(CONFIG_NTFS_FS) += ntfs.o obj-$(CONFIG_NTFS_FS) += ntfs.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \ ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o logfile.o \
mst.o namei.o super.o sysctl.o unistr.o upcase.o mft.o mst.o namei.o super.o sysctl.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.8-WIP\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.8-WIP\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG #EXTRA_CFLAGS += -DDEBUG
endif endif
ifeq ($(CONFIG_NTFS_RW),y) ifeq ($(CONFIG_NTFS_RW),y)
EXTRA_CFLAGS += -DNTFS_RW #EXTRA_CFLAGS += -DNTFS_RW
endif endif
...@@ -113,36 +113,64 @@ typedef struct { ...@@ -113,36 +113,64 @@ typedef struct {
* records (like mft records for example). * records (like mft records for example).
*/ */
typedef enum { typedef enum {
magic_BAAD = const_cpu_to_le32(0x44414142), /* BAAD == corrupt record */ /* Found in $MFT/$DATA. */
magic_CHKD = const_cpu_to_le32(0x424b4843), /* CHKD == chkdsk ??? */ magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */
magic_FILE = const_cpu_to_le32(0x454c4946), /* FILE == mft entry */ magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */
magic_HOLE = const_cpu_to_le32(0x454c4f48), /* HOLE == ? (NTFS 3.0+?) */ magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */
magic_INDX = const_cpu_to_le32(0x58444e49), /* INDX == index buffer */
/* Found in $LogFile/$DATA. */
magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */
magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */
/* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */
magic_CHKD = const_cpu_to_le32(0x424b4843), /* Modified by chkdsk. */
/* Found in all ntfs record containing records. */
magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector
transfer was detected. */
/*
* Found in $LogFile/$DATA when a page is full or 0xff bytes and is
* thus not initialized. User has to initialize the page before using
* it.
*/
magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has
to be initialized before
it can be used. */
} NTFS_RECORD_TYPES; } NTFS_RECORD_TYPES;
/* /*
* Generic magic comparison macros. Finally found a use for the ## preprocessor * Generic magic comparison macros. Finally found a use for the ## preprocessor
* operator! (-8 * operator! (-8
*/ */
#define is_magic(x, m) ( (u32)(x) == magic_##m ) #define ntfs_is_magic(x, m) ( (u32)(x) == magic_##m )
#define is_magicp(p, m) ( *(u32*)(p) == magic_##m ) #define ntfs_is_magicp(p, m) ( *(u32*)(p) == magic_##m )
/* /*
* Specialised magic comparison macros. * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above.
*/ */
#define is_baad_record(x) ( is_magic (x, BAAD) ) #define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) )
#define is_baad_recordp(p) ( is_magicp(p, BAAD) ) #define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) )
#define is_chkd_record(x) ( is_magic (x, CHKD) ) #define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) )
#define is_chkd_recordp(p) ( is_magicp(p, CHKD) ) #define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) )
#define is_file_record(x) ( is_magic (x, FILE) ) #define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) )
#define is_file_recordp(p) ( is_magicp(p, FILE) ) #define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) )
#define is_hole_record(x) ( is_magic (x, HOLE) ) #define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) )
#define is_hole_recordp(p) ( is_magicp(p, HOLE) ) #define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) )
#define is_indx_record(x) ( is_magic (x, INDX) )
#define is_indx_recordp(p) ( is_magicp(p, INDX) ) #define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) )
#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) )
#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) )
#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) )
#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) )
#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) )
#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) )
#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) )
#define is_mft_record(x) ( is_file_record(x) ) #define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) )
#define is_mft_recordp(p) ( is_file_recordp(p) ) #define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) )
/* /*
* The Update Sequence Array (usa) is an array of the u16 values which belong * The Update Sequence Array (usa) is an array of the u16 values which belong
...@@ -1513,49 +1541,46 @@ typedef enum { ...@@ -1513,49 +1541,46 @@ typedef enum {
/* /*
* The security descriptor control flags (16-bit). * The security descriptor control flags (16-bit).
* *
* SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the SID
* SID pointed to by the Owner field was provided by a * pointed to by the Owner field was provided by a defaulting mechanism
* defaulting mechanism rather than explicitly provided by the * rather than explicitly provided by the original provider of the
* original provider of the security descriptor. This may * security descriptor. This may affect the treatment of the SID with
* affect the treatment of the SID with respect to inheritence * respect to inheritence of an owner.
* of an owner. *
* * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the SID in
* SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the * the Group field was provided by a defaulting mechanism rather than
* SID in the Group field was provided by a defaulting mechanism * explicitly provided by the original provider of the security
* rather than explicitly provided by the original provider of * descriptor. This may affect the treatment of the SID with respect to
* the security descriptor. This may affect the treatment of * inheritence of a primary group.
* the SID with respect to inheritence of a primary group. *
* * SE_DACL_PRESENT - This boolean flag, when set, indicates that the security
* SE_DACL_PRESENT - This boolean flag, when set, indicates that the * descriptor contains a discretionary ACL. If this flag is set and the
* security descriptor contains a discretionary ACL. If this * Dacl field of the SECURITY_DESCRIPTOR is null, then a null ACL is
* flag is set and the Dacl field of the SECURITY_DESCRIPTOR is * explicitly being specified.
* null, then a null ACL is explicitly being specified. *
* * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the ACL
* SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the * pointed to by the Dacl field was provided by a defaulting mechanism
* ACL pointed to by the Dacl field was provided by a defaulting * rather than explicitly provided by the original provider of the
* mechanism rather than explicitly provided by the original * security descriptor. This may affect the treatment of the ACL with
* provider of the security descriptor. This may affect the * respect to inheritence of an ACL. This flag is ignored if the
* treatment of the ACL with respect to inheritence of an ACL. * DaclPresent flag is not set.
* This flag is ignored if the DaclPresent flag is not set. *
* * SE_SACL_PRESENT - This boolean flag, when set, indicates that the security
* SE_SACL_PRESENT - This boolean flag, when set, indicates that the * descriptor contains a system ACL pointed to by the Sacl field. If this
* security descriptor contains a system ACL pointed to by the * flag is set and the Sacl field of the SECURITY_DESCRIPTOR is null, then
* Sacl field. If this flag is set and the Sacl field of the * an empty (but present) ACL is being specified.
* SECURITY_DESCRIPTOR is null, then an empty (but present) *
* ACL is being specified. * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the ACL
* * pointed to by the Sacl field was provided by a defaulting mechanism
* SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the * rather than explicitly provided by the original provider of the
* ACL pointed to by the Sacl field was provided by a defaulting * security descriptor. This may affect the treatment of the ACL with
* mechanism rather than explicitly provided by the original * respect to inheritence of an ACL. This flag is ignored if the
* provider of the security descriptor. This may affect the * SaclPresent flag is not set.
* treatment of the ACL with respect to inheritence of an ACL. *
* This flag is ignored if the SaclPresent flag is not set. * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the security
* * descriptor is in self-relative form. In this form, all fields of the
* SE_SELF_RELATIVE - This boolean flag, when set, indicates that the * security descriptor are contiguous in memory and all pointer fields are
* security descriptor is in self-relative form. In this form, * expressed as offsets from the beginning of the security descriptor.
* all fields of the security descriptor are contiguous in memory
* and all pointer fields are expressed as offsets from the
* beginning of the security descriptor.
*/ */
typedef enum { typedef enum {
SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001), SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001),
......
/*
* logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project.
*
* Copyright (c) 2002-2004 Anton Altaparmakov.
*
* 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
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef NTFS_RW
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/highmem.h>
#include <linux/buffer_head.h>
#include "logfile.h"
#include "volume.h"
#include "ntfs.h"
#include "debug.h"
/**
* ntfs_check_restart_page_header - check the page header for consistency
* @vi: $LogFile inode to which the restart page header belongs
* @rp: restart page header to check
* @pos: position in @vi at which the restart page header resides
*
* Check the restart page header @rp for consistency and return TRUE if it is
* consistent and FALSE otherwise.
*
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
* require the full restart page.
*/
static BOOL ntfs_check_restart_page_header(struct inode *vi,
RESTART_PAGE_HEADER *rp, s64 pos)
{
u32 logfile_system_page_size, logfile_log_page_size;
u16 usa_count, usa_ofs, usa_end, ra_ofs;
ntfs_debug("Entering.");
/*
* If the system or log page sizes are smaller than the ntfs block size
* or either is not a power of 2 we cannot handle this log file.
*/
logfile_system_page_size = le32_to_cpu(rp->system_page_size);
logfile_log_page_size = le32_to_cpu(rp->log_page_size);
if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
logfile_log_page_size < NTFS_BLOCK_SIZE ||
logfile_system_page_size &
(logfile_system_page_size - 1) ||
logfile_log_page_size & (logfile_log_page_size - 1)) {
ntfs_error(vi->i_sb, "$LogFile uses unsupported page size.");
return FALSE;
}
/*
* We must be either at !pos (1st restart page) or at pos = system page
* size (2nd restart page).
*/
if (pos && pos != logfile_system_page_size) {
ntfs_error(vi->i_sb, "Found restart area in incorrect "
"position in $LogFile.");
return FALSE;
}
/* We only know how to handle version 1.1. */
if (sle16_to_cpu(rp->major_ver) != 1 ||
sle16_to_cpu(rp->minor_ver) != 1) {
ntfs_error(vi->i_sb, "$LogFile version %i.%i is not "
"supported. (This driver supports version "
"1.1 only.)", (int)sle16_to_cpu(rp->major_ver),
(int)sle16_to_cpu(rp->minor_ver));
return FALSE;
}
/* Verify the size of the update sequence array. */
usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
if (usa_count != le16_to_cpu(rp->usa_count)) {
ntfs_error(vi->i_sb, "$LogFile restart page specifies "
"inconsistent update sequence array count.");
return FALSE;
}
/* Verify the position of the update sequence array. */
usa_ofs = le16_to_cpu(rp->usa_ofs);
usa_end = usa_ofs + usa_count * sizeof(u16);
if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
ntfs_error(vi->i_sb, "$LogFile restart page specifies "
"inconsistent update sequence array offset.");
return FALSE;
}
/*
* Verify the position of the restart area. It must be:
* - aligned to 8-byte boundary,
* - after the update sequence array, and
* - within the system page size.
*/
ra_ofs = le16_to_cpu(rp->restart_area_offset);
if (ra_ofs & 7 || ra_ofs < usa_end ||
ra_ofs > logfile_system_page_size) {
ntfs_error(vi->i_sb, "$LogFile restart page specifies "
"inconsistent restart area offset.");
return FALSE;
}
/*
* Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
* set.
*/
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
ntfs_error(vi->i_sb, "$LogFile restart page is not modified "
"chkdsk but a chkdsk LSN is specified.");
return FALSE;
}
ntfs_debug("Done.");
return TRUE;
}
/**
* ntfs_check_restart_area - check the restart area for consistency
* @vi: $LogFile inode to which the restart page belongs
* @rp: restart page whose restart area to check
*
* Check the restart area of the restart page @rp for consistency and return
* TRUE if it is consistent and FALSE otherwise.
*
* This function assumes that the restart page header has already been
* consistency checked.
*
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
* require the full restart page.
*/
static BOOL ntfs_check_restart_area(struct inode *vi, RESTART_PAGE_HEADER *rp)
{
u64 file_size;
RESTART_AREA *ra;
u16 ra_ofs, ra_len, ca_ofs;
u8 fs_bits;
ntfs_debug("Entering.");
ra_ofs = le16_to_cpu(rp->restart_area_offset);
ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
/*
* Everything before ra->file_size must be before the first word
* protected by an update sequence number. This ensures that it is
* safe to access ra->client_array_offset.
*/
if (ra_ofs + offsetof(RESTART_AREA, file_size) >
NTFS_BLOCK_SIZE - sizeof(u16)) {
ntfs_error(vi->i_sb, "$LogFile restart area specifies "
"inconsistent file offset.");
return FALSE;
}
/*
* Now that we can access ra->client_array_offset, make sure everything
* up to the log client array is before the first word protected by an
* update sequence number. This ensures we can access all of the
* restart area elements safely. Also, the client array offset must be
* aligned to an 8-byte boundary.
*/
ca_ofs = le16_to_cpu(ra->client_array_offset);
if (((ca_ofs + 7) & ~7) != ca_ofs ||
ra_ofs + ca_ofs > NTFS_BLOCK_SIZE - sizeof(u16)) {
ntfs_error(vi->i_sb, "$LogFile restart area specifies "
"inconsistent client array offset.");
return FALSE;
}
/*
* The restart area must end within the system page size both when
* calculated manually and as specified by ra->restart_area_length.
* Also, the calculated length must not exceed the specified length.
*/
ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
sizeof(LOG_CLIENT_RECORD);
if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) ||
ra_ofs + le16_to_cpu(ra->restart_area_length) >
le32_to_cpu(rp->system_page_size) ||
ra_len > le16_to_cpu(ra->restart_area_length)) {
ntfs_error(vi->i_sb, "$LogFile restart area is out of bounds "
"of the system page size specified by the "
"restart page header and/or the specified "
"restart area length is inconsistent.");
return FALSE;
}
/*
* The ra->client_free_list and ra->client_in_use_list must be either
* LOGFILE_NO_CLIENT or less than ra->log_clients or they are
* overflowing the client array.
*/
if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
le16_to_cpu(ra->client_free_list) >=
le16_to_cpu(ra->log_clients)) ||
(ra->client_in_use_list != LOGFILE_NO_CLIENT &&
le16_to_cpu(ra->client_in_use_list) >=
le16_to_cpu(ra->log_clients))) {
ntfs_error(vi->i_sb, "$LogFile restart area specifies "
"overflowing client free and/or in use lists.");
return FALSE;
}
/*
* Check ra->seq_number_bits against ra->file_size for consistency.
* We cannot just use ffs() because the file size is not a power of 2.
*/
file_size = (u64)sle64_to_cpu(ra->file_size);
fs_bits = 0;
while (file_size) {
file_size >>= 1;
fs_bits++;
}
if (le32_to_cpu(ra->seq_number_bits) != 67 - fs_bits) {
ntfs_error(vi->i_sb, "$LogFile restart area specifies "
"inconsistent sequence number bits.");
return FALSE;
}
/* The log record header length must be a multiple of 8. */
if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
le16_to_cpu(ra->log_record_header_length)) {
ntfs_error(vi->i_sb, "$LogFile restart area specifies "
"inconsistent log record header length.");
return FALSE;
}
/* Dito for the log page data offset. */
if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
le16_to_cpu(ra->log_page_data_offset)) {
ntfs_error(vi->i_sb, "$LogFile restart area specifies "
"inconsistent log page data offset.");
return FALSE;
}
ntfs_debug("Done.");
return TRUE;
}
/**
* ntfs_check_log_client_array - check the log client array for consistency
* @vi: $LogFile inode to which the restart page belongs
* @rp: restart page whose log client array to check
*
* Check the log client array of the restart page @rp for consistency and
* return TRUE if it is consistent and FALSE otherwise.
*
* This function assumes that the restart page header and the restart area have
* already been consistency checked.
*
* Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
* function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
* restart page and the page must be multi sector transfer deprotected.
*/
static BOOL ntfs_check_log_client_array(struct inode *vi,
RESTART_PAGE_HEADER *rp)
{
RESTART_AREA *ra;
LOG_CLIENT_RECORD *ca, *cr;
u16 nr_clients, idx;
BOOL in_free_list, idx_is_first;
ntfs_debug("Entering.");
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
ca = (LOG_CLIENT_RECORD*)((u8*)ra +
le16_to_cpu(ra->client_array_offset));
/*
* Check the ra->client_free_list first and then check the
* ra->client_in_use_list. Check each of the log client records in
* each of the lists and check that the array does not overflow the
* ra->log_clients value. Also keep track of the number of records
* visited as there cannot be more than ra->log_clients records and
* that way we detect eventual loops in within a list.
*/
nr_clients = le16_to_cpu(ra->log_clients);
idx = le16_to_cpu(ra->client_free_list);
in_free_list = TRUE;
check_list:
for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
idx = le16_to_cpu(cr->next_client)) {
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
goto err_out;
/* Set @cr to the current log client record. */
cr = ca + idx;
/* The first log client record must not have a prev_client. */
if (idx_is_first) {
if (cr->prev_client != LOGFILE_NO_CLIENT)
goto err_out;
idx_is_first = FALSE;
}
}
/* Switch to and check the in use list if we just did the free list. */
if (in_free_list) {
in_free_list = FALSE;
idx = le16_to_cpu(ra->client_in_use_list);
goto check_list;
}
ntfs_debug("Done.");
return TRUE;
err_out:
ntfs_error(vi->i_sb, "$LogFile log client array is corrupt.");
return FALSE;
}
/**
* ntfs_check_and_load_restart_page - check the restart page for consistency
* @vi: $LogFile inode to which the restart page belongs
* @rp: restart page to check
* @pos: position in @vi at which the restart page resides
* @wrp: copy of the multi sector transfer deprotected restart page
*
* Check the restart page @rp for consistency and return TRUE if it is
* consistent and FALSE otherwise.
*
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
* require the full restart page.
*
* 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,
* *@wrp is undefined.
*/
static BOOL ntfs_check_and_load_restart_page(struct inode *vi,
RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp)
{
RESTART_AREA *ra;
RESTART_PAGE_HEADER *trp;
int size;
BOOL ret;
ntfs_debug("Entering.");
/* Check the restart page header for consistency. */
if (!ntfs_check_restart_page_header(vi, rp, pos)) {
/* Error output already done inside the function. */
return FALSE;
}
/* Check the restart area for consistency. */
if (!ntfs_check_restart_area(vi, rp)) {
/* Error output already done inside the function. */
return FALSE;
}
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
/*
* Allocate a buffer to store the whole restart page so we can multi
* sector transfer deprotect it.
*/
trp = ntfs_malloc_nofs(le32_to_cpu(rp->system_page_size));
if (!trp) {
ntfs_error(vi->i_sb, "Failed to allocate memory for $LogFile "
"restart page buffer.");
return FALSE;
}
/*
* Read the whole of the restart page into the buffer. If it fits
* completely inside @rp, just copy it from there. Otherwise map all
* the required pages and copy the data from them.
*/
size = PAGE_CACHE_SIZE - (pos & ~PAGE_CACHE_MASK);
if (size >= le32_to_cpu(rp->system_page_size)) {
memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
} else {
pgoff_t idx;
struct page *page;
int have_read, to_read;
/* First copy what we already have in @rp. */
memcpy(trp, rp, size);
/* Copy the remaining data one page at a time. */
have_read = size;
to_read = le32_to_cpu(rp->system_page_size) - size;
idx = (pos + size) >> PAGE_CACHE_SHIFT;
BUG_ON((pos + size) & ~PAGE_CACHE_MASK);
do {
page = ntfs_map_page(vi->i_mapping, idx);
if (IS_ERR(page)) {
ntfs_error(vi->i_sb, "Error mapping $LogFile "
"page (index %lu).", idx);
goto err_out;
}
size = min_t(int, to_read, PAGE_CACHE_SIZE);
memcpy((u8*)trp + have_read, page_address(page), size);
ntfs_unmap_page(page);
have_read += size;
to_read -= size;
idx++;
} while (to_read > 0);
}
/* Perform the multi sector transfer deprotection on the buffer. */
if (post_read_mst_fixup((NTFS_RECORD*)trp,
le32_to_cpu(rp->system_page_size))) {
ntfs_error(vi->i_sb, "Multi sector transfer error detected in "
"$LogFile restart page.");
goto err_out;
}
/* 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.");
return ret;
err_out:
ntfs_free(trp);
return FALSE;
}
/**
* ntfs_ckeck_logfile - check in the journal if the volume is consistent
* @log_vi: struct inode of loaded journal $LogFile to check
*
* Check the $LogFile journal for consistency and return TRUE if it is
* consistent and FALSE if not.
*
* 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(struct inode *log_vi)
{
s64 size, pos, rstr1_pos, rstr2_pos;
ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
struct address_space *mapping = log_vi->i_mapping;
struct page *page = NULL;
u8 *kaddr = NULL;
RESTART_PAGE_HEADER *rstr1_ph = NULL;
RESTART_PAGE_HEADER *rstr2_ph = NULL;
int log_page_size, log_page_mask, ofs;
BOOL logfile_is_empty = TRUE;
BOOL rstr1_found = FALSE;
BOOL rstr2_found = FALSE;
u8 log_page_bits;
ntfs_debug("Entering.");
/* An empty $LogFile must have been clean before it got emptied. */
if (NVolLogFileEmpty(vol))
goto is_empty;
size = log_vi->i_size;
/* Make sure the file doesn't exceed the maximum allowed size. */
if (size > MaxLogFileSize)
size = MaxLogFileSize;
/*
* Truncate size to a multiple of the page cache size or the default
* log page size if the page cache size is between the default log page
* log page size if the page cache size is between the default log page
* size and twice that.
*/
if (PAGE_CACHE_SIZE >= DefaultLogPageSize && PAGE_CACHE_SIZE <=
DefaultLogPageSize * 2)
log_page_size = DefaultLogPageSize;
else
log_page_size = PAGE_CACHE_SIZE;
log_page_mask = log_page_size - 1;
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_error(vol->sb, "$LogFile is too small.");
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 uninitialzed records, the log file can be assumed
* to be empty.
*/
for (pos = 0; pos < size; pos <<= 1) {
pgoff_t idx = pos >> PAGE_CACHE_SHIFT;
if (!page || page->index != idx) {
if (page)
ntfs_unmap_page(page);
page = ntfs_map_page(mapping, idx);
if (IS_ERR(page)) {
ntfs_error(vol->sb, "Error mapping $LogFile "
"page (index %lu).", idx);
return FALSE;
}
}
kaddr = (u8*)page_address(page) + (pos & ~PAGE_CACHE_MASK);
/*
* 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(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(kaddr))
break;
/*
* A modified by chkdsk restart page means we cannot handle
* this log file.
*/
if (ntfs_is_chkd_recordp(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(kaddr)) {
/* Skip to the minimum page size for the next one. */
if (!pos)
pos = NTFS_BLOCK_SIZE >> 1;
continue;
}
/* We now know we have a restart page. */
if (!pos) {
rstr1_found = TRUE;
rstr1_pos = pos;
} else {
if (rstr2_found) {
ntfs_error(vol->sb, "Found more than two "
"restart pages in $LogFile.");
goto err_out;
}
rstr2_found = TRUE;
rstr2_pos = pos;
}
/*
* Check the restart page for consistency and get a copy of the
* complete multi sector transfer deprotected restart page.
*/
if (!ntfs_check_and_load_restart_page(log_vi,
(RESTART_PAGE_HEADER*)kaddr, pos,
!pos ? &rstr1_ph : &rstr2_ph)) {
/* Error output already done inside the function. */
goto err_out;
}
/*
* 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)
pos = le32_to_cpu(rstr1_ph->system_page_size) >> 1;
}
if (page) {
ntfs_unmap_page(page);
page = NULL;
}
if (logfile_is_empty) {
NVolSetLogFileEmpty(vol);
is_empty:
ntfs_debug("Done. ($LogFile is empty.)");
return TRUE;
}
if (!rstr1_found || !rstr2_found) {
ntfs_error(vol->sb, "Did not find two restart pages in "
"$LogFile.");
goto err_out;
}
/*
* The two restart areas must be identical except for the update
* sequence number.
*/
ofs = le16_to_cpu(rstr1_ph->usa_ofs);
if (memcmp(rstr1_ph, rstr2_ph, ofs) || (ofs += sizeof(u16),
memcmp((u8*)rstr1_ph + ofs, (u8*)rstr2_ph + ofs,
le32_to_cpu(rstr1_ph->system_page_size) - ofs))) {
ntfs_error(vol->sb, "The two restart pages in $LogFile do not "
"match.");
goto err_out;
}
ntfs_free(rstr1_ph);
ntfs_free(rstr2_ph);
/* All consistency checks passed. */
ntfs_debug("Done.");
return TRUE;
err_out:
if (page)
ntfs_unmap_page(page);
if (rstr1_ph)
ntfs_free(rstr1_ph);
if (rstr2_ph)
ntfs_free(rstr2_ph);
return FALSE;
}
/**
* ntfs_is_logfile_clean - check in the journal if the volume is clean
* @log_vi: struct inode of loaded journal $LogFile to check
*
* 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 preceeding 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(struct inode *log_vi)
{
ntfs_volume *vol = NTFS_SB(log_vi->i_sb);
struct page *page;
RESTART_PAGE_HEADER *rp;
RESTART_AREA *ra;
ntfs_debug("Entering.");
/* An empty $LogFile must have been clean before it got emptied. */
if (NVolLogFileEmpty(vol)) {
ntfs_debug("Done. ($LogFile is empty.)");
return TRUE;
}
/*
* Read the first restart page. It will be possibly incomplete and
* will not be multi sector transfer deprotected but we only need the
* first NTFS_BLOCK_SIZE bytes so it does not matter.
*/
page = ntfs_map_page(log_vi->i_mapping, 0);
if (IS_ERR(page)) {
ntfs_error(vol->sb, "Error mapping $LogFile page (index 0).");
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));
/*
* 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_debug("Done. $LogFile indicates a dirty shutdown.");
goto err_out;
}
ntfs_unmap_page(page);
/* $LogFile indicates a clean shutdown. */
ntfs_debug("Done. $LogFile indicates a clean shutdown.");
return TRUE;
err_out:
ntfs_unmap_page(page);
return FALSE;
}
#endif /* NTFS_RW */
/*
* logfile.h - Defines for NTFS kernel journal ($LogFile) handling. Part of
* the Linux-NTFS project.
*
* Copyright (c) 2000-2004 Anton Altaparmakov
*
* 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
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _LINUX_NTFS_LOGFILE_H
#define _LINUX_NTFS_LOGFILE_H
#ifdef NTFS_RW
#include <linux/fs.h>
#include "types.h"
#include "endian.h"
#include "layout.h"
/*
* Journal ($LogFile) organization:
*
* Two restart areas present in the first two pages (restart pages, one restart
* area in each page). When the volume is dismounted they should be identical,
* except for the update sequence array which usually has a different update
* sequence number.
*
* These are followed by log records organized in pages headed by a log record
* header going up to log file size. Not all pages contain log records when a
* volume is first formatted, but as the volume ages, all records will be used.
* When the log file fills up, the records at the beginning are purged (by
* modifying the oldest_lsn to a higher value presumably) and writing begins
* at the beginning of the file. Effectively, the log file is viewed as a
* circular entity.
*
* NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
* versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We
* probably only want to support 1.1 as this seems to be the current version
* and we don't know how that differs from the older versions. The only
* exception is if the journal is clean as marked by the two restart pages
* then it doesn't matter whether we are on an earlier version. We can just
* reinitialize the logfile and start again with version 1.1.
*/
/* Some $LogFile related constants. */
#define MaxLogFileSize 0x100000000ULL
#define DefaultLogPageSize 4096
#define MinLogRecordPages 48
/*
* Log file restart page header (begins the restart area).
*/
typedef struct {
/*Ofs*/
/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */
/* 4*/ u16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
When creating, set this to be immediately
after this header structure (without any
alignment). */
/* 6*/ u16 usa_count; /* See NTFS_RECORD definition in layout.h. */
/* 8*/ LSN chkdsk_lsn; /* The last log file sequence number found by
chkdsk. Only used when the magic is changed
to "CHKD". Otherwise this is zero. */
/* 16*/ u32 system_page_size; /* Byte size of system pages when the log file
was created, has to be >= 512 and a power of
2. Use this to calculate the required size
of the usa (usa_count) and add it to usa_ofs.
Then verify that the result is less than the
value of the restart_area_offset. */
/* 20*/ u32 log_page_size; /* Byte size of log file pages, has to be >=
512 and a power of 2. The default is 4096
and is used when the system page size is
between 4096 and 8192. Otherwise this is
set to the system page size instead. */
/* 24*/ u16 restart_area_offset;/* Byte offset from the start of this header to
the RESTART_AREA. Value has to be aligned
to 8-byte boundary. When creating, set this
to be after the usa. */
/* 26*/ s16 minor_ver; /* Log file minor version. Only check if major
version is 1. */
/* 28*/ s16 major_ver; /* Log file major version. We only support
version 1.1. */
/* sizeof() = 30 (0x1e) bytes */
} __attribute__ ((__packed__)) RESTART_PAGE_HEADER;
/*
* Constant for the log client indices meaning that there are no client records
* in this particular client array. Also inside the client records themselves,
* this means that there are no client records preceding or following this one.
*/
#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff)
#define LOGFILE_NO_CLIENT_CPU 0xffff
/*
* These are the so far known RESTART_AREA_* flags (16-bit) which contain
* information about the log file in which they are present.
*/
typedef enum {
RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002),
REST_AREA_SPACE_FILLER = 0xffff /* Just to make flags 16-bit. */
} __attribute__ ((__packed__)) RESTART_AREA_FLAGS;
/*
* Log file restart area record. The offset of this record is found by adding
* the offset of the RESTART_PAGE_HEADER to the restart_area_offset value found
* in it. See notes at restart_area_offset above.
*/
typedef struct {
/*Ofs*/
/* 0*/ LSN current_lsn; /* The current, i.e. last LSN inside the log
when the restart area was last written.
This happens often but what is the interval?
Is it just fixed time or is it every time a
check point is written or somethine else?
On create set to 0. */
/* 8*/ u16 log_clients; /* Number of log client records in the array of
log client records which follows this
restart area. Must be 1. */
/* 10*/ u16 client_free_list; /* The index of the first free log client record
in the array of log client records.
LOGFILE_NO_CLIENT means that there are no
free log client records in the array.
If != LOGFILE_NO_CLIENT, check that
log_clients > client_free_list. On Win2k
and presumably earlier, on a clean volume
this is != LOGFILE_NO_CLIENT, and it should
be 0, i.e. the first (and only) client
record is free and thus the logfile is
closed and hence clean. A dirty volume
would have left the logfile open and hence
this would be LOGFILE_NO_CLIENT. On WinXP
and presumably later, the logfile is always
open, even on clean shutdown so this should
always be LOGFILE_NO_CLIENT. */
/* 12*/ u16 client_in_use_list; /* The index of the first in-use log client
record in the array of log client records.
LOGFILE_NO_CLIENT means that there are no
in-use log client records in the array. If
!= LOGFILE_NO_CLIENT check that log_clients
> client_in_use_list. On Win2k and
presumably earlier, on a clean volume this
is LOGFILE_NO_CLIENT, i.e. there are no
client records in use and thus the logfile
is closed and hence clean. A dirty volume
would have left the logfile open and hence
this would be != LOGFILE_NO_CLIENT, and it
should be 0, i.e. the first (and only)
client record is in use. On WinXP and
presumably later, the logfile is always
open, even on clean shutdown so this should
always be 0. */
/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k
and presumably earlier this is always 0. On
WinXP and presumably later, if the logfile
was shutdown cleanly, the second bit,
RESTART_VOLUME_IS_CLEAN, is set. This bit
is cleared when the volume is mounted by
WinXP and set when the volume is dismounted,
thus if the logfile is dirty, this bit is
clear. Thus we don't need to check the
Windows version to determine if the logfile
is clean. Instead if the logfile is closed,
we know it must be clean. If it is open and
this bit is set, we also know it must be
clean. If on the other hand the logfile is
open and this bit is clear, we can be almost
certain that the logfile is dirty. */
/* 16*/ u32 seq_number_bits; /* How many bits to use for the sequence
number. This is calculated as 67 - the
number of bits required to store the logfile
size in bytes and this can be used in with
the specified file_size as a consistency
check. */
/* 20*/ u16 restart_area_length;/* Length of the restart area including the
client array. Following checks required if
version matches. Otherwise, skip them.
restart_area_offset + restart_area_length
has to be <= system_page_size. Also,
restart_area_length has to be >=
client_array_offset + (log_clients *
sizeof(log client record)). */
/* 22*/ u16 client_array_offset;/* Offset from the start of this record to
the first log client record if versions are
matched. When creating, set this to be
after this restart area structure, aligned
to 8-bytes boundary. If the versions do not
match, this is ignored and the offset is
assumed to be (sizeof(RESTART_AREA) + 7) &
~7, i.e. rounded up to first 8-byte
boundary. Either way, client_array_offset
has to be aligned to an 8-byte boundary.
Also, restart_area_offset +
client_array_offset has to be <= 510.
Finally, client_array_offset + (log_clients
* sizeof(log client record)) has to be <=
system_page_size. On Win2k and presumably
earlier, this is 0x30, i.e. immediately
following this record. On WinXP and
presumably later, this is 0x40, i.e. there
are 16 extra bytes between this record and
the client array. This probably means that
the RESTART_AREA record is actually bigger
in WinXP and later. */
/* 24*/ s64 file_size; /* Usable byte size of the log file. If the
restart_area_offset + the offset of the
file_size are > 510 then corruption has
occured. This is the very first check when
starting with the restart_area as if it
fails it means that some of the above values
will be corrupted by the multi sector
transfer protection. The file_size has to
be rounded down to be a multiple of the
log_page_size in the RESTART_PAGE_HEADER and
then it has to be at least big enough to
store the two restart pages and 48 (0x30)
log record pages. */
/* 32*/ u32 last_lsn_data_length;/* Length of data of last LSN, not including
the log record header. On create set to
0. */
/* 36*/ u16 log_record_header_length;/* Byte size of the log record header. If
the version matches then check that the
value of log_record_header_length is a
multiple of 8, i.e.
(log_record_header_length + 7) & ~7 ==
log_record_header_length. When creating set
it to sizeof(LOG_RECORD_HEADER), aligned to
8 bytes. */
/* 38*/ u16 log_page_data_offset;/* Offset to the start of data in a log record
page. Must be a multiple of 8. On create
set it to immediately after the update
sequence array of the log record page. */
/* 40*/ u32 restart_log_open_count;/* A counter that gets incremented every time
the logfile is restarted which happens at
mount time when the logfile is opened. When
creating set to a random value. Win2k sets
it to the low 32 bits of the current system
time in NTFS format (see time.h). */
/* 44*/ u32 reserved; /* Reserved/alignment to 8-byte boundary. */
/* sizeof() = 48 (0x30) bytes */
} __attribute__ ((__packed__)) RESTART_AREA;
/*
* Log client record. The offset of this record is found by adding the offset
* of the RESTART_AREA to the client_array_offset value found in it.
*/
typedef struct {
/*Ofs*/
/* 0*/ LSN oldest_lsn; /* Oldest LSN needed by this client. On create
set to 0. */
/* 8*/ LSN client_restart_lsn; /* LSN at which this client needs to restart
the volume, i.e. the current position within
the log file. At present, if clean this
should = current_lsn in restart area but it
probably also = current_lsn when dirty most
of the time. At create set to 0. */
/* 16*/ u16 prev_client; /* The offset to the previous log client record
in the array of log client records.
LOGFILE_NO_CLIENT means there is no previous
client record, i.e. this is the first one.
This is always LOGFILE_NO_CLIENT. */
/* 18*/ u16 next_client; /* The offset to the next log client record in
the array of log client records.
LOGFILE_NO_CLIENT means there are no next
client records, i.e. this is the last one.
This is always LOGFILE_NO_CLIENT. */
/* 20*/ u16 seq_number; /* On Win2k and presumably earlier, this is set
to zero every time the logfile is restarted
and it is incremented when the logfile is
closed at dismount time. Thus it is 0 when
dirty and 1 when clean. On WinXP and
presumably later, this is always 0. */
/* 22*/ u8 reserved[6]; /* Reserved/alignment. */
/* 28*/ u32 client_name_length; /* Length of client name in bytes. Should
always be 8. */
/* 32*/ uchar_t client_name[64];/* Name of the client in Unicode. Should
always be "NTFS" with the remaining bytes
set to 0. */
/* sizeof() = 160 (0xa0) bytes */
} __attribute__ ((__packed__)) LOG_CLIENT_RECORD;
extern BOOL ntfs_check_logfile(struct inode *log_vi);
extern BOOL ntfs_is_logfile_clean(struct inode *log_vi);
#endif /* NTFS_RW */
#endif /* _LINUX_NTFS_LOGFILE_H */
/* /*
* malloc.h - NTFS kernel memory handling. Part of the Linux-NTFS project. * malloc.h - NTFS kernel memory handling. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2004 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
...@@ -38,7 +38,7 @@ static inline void *ntfs_malloc_nofs(unsigned long size) ...@@ -38,7 +38,7 @@ static inline void *ntfs_malloc_nofs(unsigned long size)
{ {
if (likely(size <= PAGE_SIZE)) { if (likely(size <= PAGE_SIZE)) {
if (likely(size)) { if (likely(size)) {
/* kmalloc() has per-CPU caches so if faster for now. */ /* kmalloc() has per-CPU caches so is faster for now. */
return kmalloc(PAGE_SIZE, GFP_NOFS); return kmalloc(PAGE_SIZE, GFP_NOFS);
/* return (void *)__get_free_page(GFP_NOFS | /* return (void *)__get_free_page(GFP_NOFS |
__GFP_HIGHMEM); */ __GFP_HIGHMEM); */
...@@ -61,4 +61,3 @@ static inline void ntfs_free(void *addr) ...@@ -61,4 +61,3 @@ static inline void ntfs_free(void *addr)
} }
#endif /* _LINUX_NTFS_MALLOC_H */ #endif /* _LINUX_NTFS_MALLOC_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* mst.c - NTFS multi sector transfer protection handling code. Part of the * mst.c - NTFS multi sector transfer protection handling code. Part of the
* Linux-NTFS project. * Linux-NTFS project.
* *
* Copyright (c) 2001 Anton Altaparmakov. * Copyright (c) 2001-2004 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
...@@ -126,7 +126,8 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size) ...@@ -126,7 +126,8 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
u16 *usa_pos, *data_pos; u16 *usa_pos, *data_pos;
/* Sanity check + only fixup if it makes sense. */ /* Sanity check + only fixup if it makes sense. */
if (!b || is_baad_record(b->magic) || is_hole_record(b->magic)) if (!b || ntfs_is_baad_record(b->magic) ||
ntfs_is_hole_record(b->magic))
return -EINVAL; return -EINVAL;
/* Setup the variables. */ /* Setup the variables. */
usa_ofs = le16_to_cpu(b->usa_ofs); usa_ofs = le16_to_cpu(b->usa_ofs);
...@@ -199,4 +200,3 @@ void post_write_mst_fixup(NTFS_RECORD *b) ...@@ -199,4 +200,3 @@ void post_write_mst_fixup(NTFS_RECORD *b)
data_pos += NTFS_BLOCK_SIZE/sizeof(u16); data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
} }
} }
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "ntfs.h" #include "ntfs.h"
#include "sysctl.h" #include "sysctl.h"
#include "logfile.h"
/* Number of mounted file systems which have compression enabled. */ /* Number of mounted file systems which have compression enabled. */
static unsigned long ntfs_nr_compression_users; static unsigned long ntfs_nr_compression_users;
...@@ -318,7 +319,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ...@@ -318,7 +319,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
*/ */
if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
if (NVolErrors(vol)) { if (NVolErrors(vol)) {
ntfs_error(sb, "Volume has errors and is read-only." ntfs_error(sb, "Volume has errors and is read-only. "
"Cannot remount read-write."); "Cannot remount read-write.");
return -EROFS; return -EROFS;
} }
...@@ -728,7 +729,7 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol) ...@@ -728,7 +729,7 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol)
if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) {
if (!IS_ERR(tmp_ino)) if (!IS_ERR(tmp_ino))
iput(tmp_ino); iput(tmp_ino);
ntfs_error(vol->sb, "Failed to load $MFTMirr."); /* Caller will display error message. */
return FALSE; return FALSE;
} }
/* /*
...@@ -773,6 +774,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol) ...@@ -773,6 +774,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
run_list_element *rl, rl2[2]; run_list_element *rl, rl2[2];
int mrecs_per_page, i; int mrecs_per_page, i;
ntfs_debug("Entering.");
/* Compare contents of $MFT and $MFTMirr. */ /* Compare contents of $MFT and $MFTMirr. */
mrecs_per_page = PAGE_CACHE_SIZE / vol->mft_record_size; mrecs_per_page = PAGE_CACHE_SIZE / vol->mft_record_size;
BUG_ON(!mrecs_per_page); BUG_ON(!mrecs_per_page);
...@@ -808,7 +810,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol) ...@@ -808,7 +810,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
++index; ++index;
} }
/* Make sure the record is ok. */ /* Make sure the record is ok. */
if (is_baad_recordp(kmft)) { if (ntfs_is_baad_recordp(kmft)) {
ntfs_error(sb, "Incomplete multi sector transfer " ntfs_error(sb, "Incomplete multi sector transfer "
"detected in mft record %i.", i); "detected in mft record %i.", i);
mm_unmap_out: mm_unmap_out:
...@@ -817,7 +819,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol) ...@@ -817,7 +819,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
ntfs_unmap_page(mft_page); ntfs_unmap_page(mft_page);
return FALSE; return FALSE;
} }
if (is_baad_recordp(kmirr)) { if (ntfs_is_baad_recordp(kmirr)) {
ntfs_error(sb, "Incomplete multi sector transfer " ntfs_error(sb, "Incomplete multi sector transfer "
"detected in mft mirror record %i.", i); "detected in mft mirror record %i.", i);
goto mm_unmap_out; goto mm_unmap_out;
...@@ -869,6 +871,35 @@ static BOOL check_mft_mirror(ntfs_volume *vol) ...@@ -869,6 +871,35 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
} }
} while (rl2[i++].length); } while (rl2[i++].length);
up_read(&mirr_ni->run_list.lock); up_read(&mirr_ni->run_list.lock);
ntfs_debug("Done.");
return TRUE;
}
/**
* load_and_check_logfile - load and check the logfile inode for a volume
* @vol: ntfs super block describing device whose logfile to load
*
* Return TRUE on success or FALSE on error.
*/
static BOOL load_and_check_logfile(ntfs_volume *vol)
{
struct inode *tmp_ino;
ntfs_debug("Entering.");
tmp_ino = ntfs_iget(vol->sb, FILE_LogFile);
if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) {
if (!IS_ERR(tmp_ino))
iput(tmp_ino);
/* Caller will display error message. */
return FALSE;
}
if (!ntfs_check_logfile(tmp_ino)) {
iput(tmp_ino);
/* ntfs_check_logfile() will have displayed error output. */
return FALSE;
}
vol->logfile_ino = tmp_ino;
ntfs_debug("Done.");
return TRUE; return TRUE;
} }
...@@ -926,7 +957,7 @@ static BOOL load_and_init_upcase(ntfs_volume *vol) ...@@ -926,7 +957,7 @@ static BOOL load_and_init_upcase(ntfs_volume *vol)
goto read_partial_upcase_page; goto read_partial_upcase_page;
} }
vol->upcase_len = ino->i_size >> UCHAR_T_SIZE_BITS; vol->upcase_len = ino->i_size >> UCHAR_T_SIZE_BITS;
ntfs_debug("Read %lu bytes from $UpCase (expected %u bytes).", ntfs_debug("Read %llu bytes from $UpCase (expected %u bytes).",
ino->i_size, 64 * 1024 * sizeof(uchar_t)); ino->i_size, 64 * 1024 * sizeof(uchar_t));
iput(ino); iput(ino);
down(&ntfs_lock); down(&ntfs_lock);
...@@ -1094,25 +1125,42 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1094,25 +1125,42 @@ static BOOL load_system_files(ntfs_volume *vol)
unmap_mft_record(NTFS_I(vol->vol_ino)); unmap_mft_record(NTFS_I(vol->vol_ino));
printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver, printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver,
vol->minor_ver); vol->minor_ver);
#ifdef NTFS_RW
/* /*
* Get the inode for the logfile and empty it if this is a read-write * Get the inode for the logfile, check it and determine if the volume
* mount. * was shutdown cleanly.
*/ */
// TODO: vol->logfile_ino = ; if (!load_and_check_logfile(vol) ||
// TODO: Cleanup for error case at end of function. !ntfs_is_logfile_clean(vol->logfile_ino)) {
tmp_ino = ntfs_iget(sb, FILE_LogFile); static const char *es1 = "Failed to load $LogFile";
if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) { static const char *es2 = "$LogFile is not clean";
if (!IS_ERR(tmp_ino)) static const char *es3 = ". Mount in Windows.";
iput(tmp_ino);
ntfs_error(sb, "Failed to load $LogFile."); /* If a read-write mount, convert it to a read-only mount. */
// FIMXE: We only want to empty the thing so pointless bailing if (!(sb->s_flags & MS_RDONLY)) {
// out. Can recover/ignore. if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO |
goto iput_vol_err_out; ON_ERRORS_CONTINUE))) {
ntfs_error(sb, "%s and neither on_errors="
"continue nor on_errors="
"remount-ro was specified%s",
!vol->logfile_ino ? es1 : es2,
es3);
goto iput_logfile_err_out;
}
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
ntfs_error(sb, "%s. Mounting read-only%s",
!vol->logfile_ino ? es1 : es2, es3);
} else
ntfs_warning(sb, "%s. Will not be able to remount "
"read-write%s",
!vol->logfile_ino ? es1 : es2, es3);
/* This will prevent a read-write remount. */
NVolSetErrors(vol);
} }
// FIXME: Empty the logfile, but only if not read-only. // FIXME: Empty the logfile, but only if not read-only.
// FIXME: What happens if someone remounts rw? We need to empty the file // FIXME: What happens if someone remounts rw? We need to empty the file
// then. We need a flag to tell us whether we have done it already. // then. We need a flag to tell us whether we have done it already.
iput(tmp_ino); #endif
/* /*
* Get the inode for the attribute definitions file and parse the * Get the inode for the attribute definitions file and parse the
* attribute definitions. * attribute definitions.
...@@ -1122,7 +1170,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1122,7 +1170,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(tmp_ino)) if (!IS_ERR(tmp_ino))
iput(tmp_ino); iput(tmp_ino);
ntfs_error(sb, "Failed to load $AttrDef."); ntfs_error(sb, "Failed to load $AttrDef.");
goto iput_vol_err_out; goto iput_logfile_err_out;
} }
// FIXME: Parse the attribute definitions. // FIXME: Parse the attribute definitions.
iput(tmp_ino); iput(tmp_ino);
...@@ -1132,7 +1180,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1132,7 +1180,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(vol->root_ino)) if (!IS_ERR(vol->root_ino))
iput(vol->root_ino); iput(vol->root_ino);
ntfs_error(sb, "Failed to load root directory."); ntfs_error(sb, "Failed to load root directory.");
goto iput_vol_err_out; goto iput_logfile_err_out;
} }
/* If on NTFS versions before 3.0, we are done. */ /* If on NTFS versions before 3.0, we are done. */
if (vol->major_ver < 3) if (vol->major_ver < 3)
...@@ -1166,7 +1214,11 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1166,7 +1214,11 @@ static BOOL load_system_files(ntfs_volume *vol)
iput(vol->secure_ino); iput(vol->secure_ino);
iput_root_err_out: iput_root_err_out:
iput(vol->root_ino); iput(vol->root_ino);
iput_vol_err_out: iput_logfile_err_out:
#ifdef NTFS_RW
if (vol->logfile_ino)
iput(vol->logfile_ino);
#endif /* NTFS_RW */
iput(vol->vol_ino); iput(vol->vol_ino);
iput_lcnbmp_err_out: iput_lcnbmp_err_out:
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* types.h - Defines for NTFS Linux kernel driver specific types. * types.h - Defines for NTFS Linux kernel driver specific types.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2004 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
...@@ -23,14 +23,6 @@ ...@@ -23,14 +23,6 @@
#ifndef _LINUX_NTFS_TYPES_H #ifndef _LINUX_NTFS_TYPES_H
#define _LINUX_NTFS_TYPES_H #define _LINUX_NTFS_TYPES_H
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
#define SN(X) X /* Struct Name */
#define SC(P,N) P.N /* ShortCut: Prefix, Name */
#else
#define SN(X)
#define SC(P,N) N
#endif
/* 2-byte Unicode character type. */ /* 2-byte Unicode character type. */
typedef u16 uchar_t; typedef u16 uchar_t;
#define UCHAR_T_SIZE_BITS 1 #define UCHAR_T_SIZE_BITS 1
...@@ -42,6 +34,13 @@ typedef u16 uchar_t; ...@@ -42,6 +34,13 @@ typedef u16 uchar_t;
typedef s64 VCN; typedef s64 VCN;
typedef s64 LCN; typedef s64 LCN;
/*
* The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit
* values. We define our own type LSN, to allow for type checking and better
* code readability.
*/
typedef s64 LSN;
/** /**
* run_list_element - in memory vcn to lcn mapping array element * run_list_element - in memory vcn to lcn mapping array element
* @vcn: starting vcn of the current array element * @vcn: starting vcn of the current array element
...@@ -81,4 +80,3 @@ typedef enum { ...@@ -81,4 +80,3 @@ typedef enum {
} IGNORE_CASE_BOOL; } IGNORE_CASE_BOOL;
#endif /* _LINUX_NTFS_TYPES_H */ #endif /* _LINUX_NTFS_TYPES_H */
...@@ -116,6 +116,7 @@ typedef enum { ...@@ -116,6 +116,7 @@ typedef enum {
create filenames in the POSIX namespace. create filenames in the POSIX namespace.
Otherwise be case insensitive and create Otherwise be case insensitive and create
file names in WIN32 namespace. */ file names in WIN32 namespace. */
NV_LogFileEmpty, /* 1: $LogFile journal is empty. */
} ntfs_volume_flags; } ntfs_volume_flags;
/* /*
...@@ -140,5 +141,6 @@ static inline void NVolClear##flag(ntfs_volume *vol) \ ...@@ -140,5 +141,6 @@ static inline void NVolClear##flag(ntfs_volume *vol) \
NVOL_FNS(Errors) NVOL_FNS(Errors)
NVOL_FNS(ShowSystemFiles) NVOL_FNS(ShowSystemFiles)
NVOL_FNS(CaseSensitive) NVOL_FNS(CaseSensitive)
NVOL_FNS(LogFileEmpty)
#endif /* _LINUX_NTFS_VOLUME_H */ #endif /* _LINUX_NTFS_VOLUME_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment