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:
- Determine the mft mirror size as the number of mirrored mft records
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
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
to Meelis Roos for reporting them.
- Move %L to %ll as %L is floating point and %ll is integer which is
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,15 +2,15 @@
obj-$(CONFIG_NTFS_FS) += ntfs.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \
mst.o namei.o super.o sysctl.o unistr.o upcase.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o logfile.o \
mft.o mst.o namei.o super.o sysctl.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.8-WIP\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
#EXTRA_CFLAGS += -DDEBUG
endif
ifeq ($(CONFIG_NTFS_RW),y)
EXTRA_CFLAGS += -DNTFS_RW
#EXTRA_CFLAGS += -DNTFS_RW
endif
......@@ -10,13 +10,13 @@
* 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
* 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
* 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
*/
......@@ -47,13 +47,13 @@
/*
* Location of bootsector on partition:
* The standard NTFS_BOOT_SECTOR is on sector 0 of the partition.
* On NT4 and above there is one backup copy of the boot sector to
* be found on the last sector of the partition (not normally accessible
* from within Windows as the bootsector contained number of sectors
* The standard NTFS_BOOT_SECTOR is on sector 0 of the partition.
* On NT4 and above there is one backup copy of the boot sector to
* be found on the last sector of the partition (not normally accessible
* from within Windows as the bootsector contained number of sectors
* value is one less than the actual value!).
* On versions of NT 3.51 and earlier, the backup copy was located at
* number of sectors/2 (integer divide), i.e. in the middle of the volume.
* On versions of NT 3.51 and earlier, the backup copy was located at
* number of sectors/2 (integer divide), i.e. in the middle of the volume.
*/
/*
......@@ -113,36 +113,64 @@ typedef struct {
* records (like mft records for example).
*/
typedef enum {
magic_BAAD = const_cpu_to_le32(0x44414142), /* BAAD == corrupt record */
magic_CHKD = const_cpu_to_le32(0x424b4843), /* CHKD == chkdsk ??? */
magic_FILE = const_cpu_to_le32(0x454c4946), /* FILE == mft entry */
magic_HOLE = const_cpu_to_le32(0x454c4f48), /* HOLE == ? (NTFS 3.0+?) */
magic_INDX = const_cpu_to_le32(0x58444e49), /* INDX == index buffer */
/* Found in $MFT/$DATA. */
magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */
magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */
magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */
/* 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;
/*
* Generic magic comparison macros. Finally found a use for the ## preprocessor
* operator! (-8
*/
#define is_magic(x, m) ( (u32)(x) == magic_##m )
#define is_magicp(p, m) ( *(u32*)(p) == magic_##m )
#define ntfs_is_magic(x, m) ( (u32)(x) == 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 is_baad_recordp(p) ( is_magicp(p, BAAD) )
#define is_chkd_record(x) ( is_magic (x, CHKD) )
#define is_chkd_recordp(p) ( is_magicp(p, CHKD) )
#define is_file_record(x) ( is_magic (x, FILE) )
#define is_file_recordp(p) ( is_magicp(p, FILE) )
#define is_hole_record(x) ( is_magic (x, HOLE) )
#define is_hole_recordp(p) ( 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_file_record(x) ( ntfs_is_magic (x, FILE) )
#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) )
#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) )
#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) )
#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) )
#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) )
#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) )
#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) )
#define is_mft_record(x) ( is_file_record(x) )
#define is_mft_recordp(p) ( is_file_recordp(p) )
#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 ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) )
#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
......@@ -154,7 +182,7 @@ typedef enum {
* are set to it (during writing). If they are not, an incomplete multi sector
* transfer has occurred when the data was written.
* The maximum size for the update sequence array is fixed to:
* maximum size = usa_ofs + (usa_count * 2) = 510 bytes
* maximum size = usa_ofs + (usa_count * 2) = 510 bytes
* The 510 bytes comes from the fact that the last u16 in the array has to
* (obviously) finish before the last u16 of the first 512-byte sector.
* This formula can be used as a consistency check in that usa_ofs +
......@@ -218,7 +246,7 @@ typedef enum {
} NTFS_SYSTEM_FILES;
/*
* These are the so far known MFT_RECORD_* flags (16-bit) which contain
* These are the so far known MFT_RECORD_* flags (16-bit) which contain
* information about the mft record in which they are present.
*/
typedef enum {
......@@ -230,7 +258,7 @@ typedef enum {
/*
* mft references (aka file references or file record segment references) are
* used whenever a structure needs to refer to a record in the mft.
*
*
* A reference consists of a 48-bit index into the mft and a 16-bit sequence
* number used to detect stale references.
*
......@@ -256,7 +284,7 @@ typedef enum {
* Do I hear screams "we want 64-bit inodes!" ?!? (-;
*
* FIXME: The mft zone is defined as the first 12% of the volume. This space is
* reserved so that the mft can grow contiguously and hence doesn't become
* reserved so that the mft can grow contiguously and hence doesn't become
* fragmented. Volume free space includes the empty part of the mft zone and
* when the volume's free 88% are used up, the mft zone is shrunk by a factor
* of 2, thus making more space available for more files/data. This process is
......@@ -304,11 +332,11 @@ typedef struct {
/* 8*/ u64 lsn; /* $LogFile sequence number for this record.
Changed every time the record is modified. */
/* 16*/ u16 sequence_number; /* Number of times this mft record has been
reused. (See description for MFT_REF
reused. (See description for MFT_REF
above.) NOTE: The increment (skipping zero)
is done when the file is deleted. NOTE: If
this is zero it is left zero. */
/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of
/* 18*/ u16 link_count; /* Number of hard links, i.e. the number of
directory entries referencing this record.
NOTE: Only used in mft base records.
NOTE: When deleting a directory entry we
......@@ -373,7 +401,7 @@ typedef struct {
* revealing choice of symbol... (-;
*/
typedef enum {
AT_UNUSED = const_cpu_to_le32( 0),
AT_UNUSED = const_cpu_to_le32( 0),
AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10),
AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20),
AT_FILE_NAME = const_cpu_to_le32( 0x30),
......@@ -408,13 +436,13 @@ typedef enum {
* for what I mean but COLLATION_UNICODE_STRING would not give any special
* treatment to any characters at all, but this is speculation.
* COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key
* values. E.g. used for $SII index in FILE_Secure, which sorts by
* security_id (u32).
* values. E.g. used for $SII index in FILE_Secure, which sorts by
* security_id (u32).
* COLLATION_NTOFS_SID - Sorting is done according to ascending SID values.
* E.g. used for $O index in FILE_Extend/$Quota.
* E.g. used for $O index in FILE_Extend/$Quota.
* COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash
* values and second by ascending security_id values. E.g. used for $SDH
* index in FILE_Secure.
* values and second by ascending security_id values. E.g. used for $SDH
* index in FILE_Secure.
* COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending
* u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which
* sorts by object_id (16-byte), by splitting up the object_id in four
......@@ -489,7 +517,7 @@ typedef struct {
} __attribute__ ((__packed__)) ATTR_DEF;
/*
* Attribute flags (16-bit).
* Attribute flags (16-bit).
*/
typedef enum {
ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001),
......@@ -600,7 +628,7 @@ typedef struct {
boundary. */
/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */
/* 14*/ u16 instance; /* The instance of this attribute record. This
number is unique within this mft record (see
number is unique within this mft record (see
MFT_RECORD/next_attribute_instance notes in
in mft.h for more details). */
/* 16*/ union {
......@@ -610,7 +638,7 @@ typedef struct {
/* 20 */ u16 value_offset; /* Byte offset of the attribute
value from the start of the
attribute record. When creating,
align to 8-byte boundary if we
align to 8-byte boundary if we
have a name present as this might
not have a length of a multiple
of 8-bytes. */
......@@ -703,7 +731,7 @@ typedef enum {
FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000),
FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7),
/* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the
/* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the
FILE_ATTR_DEVICE and preserves everything else. This mask
is used to obtain all flags that are valid for reading. */
FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7),
......@@ -740,9 +768,9 @@ typedef enum {
* NOTE: Always resident.
* NOTE: Present in all base file records on a volume.
* NOTE: There is conflicting information about the meaning of each of the time
* fields but the meaning as defined below has been verified to be
* correct by practical experimentation on Windows NT4 SP6a and is hence
* assumed to be the one and only correct interpretation.
* fields but the meaning as defined below has been verified to be
* correct by practical experimentation on Windows NT4 SP6a and is hence
* assumed to be the one and only correct interpretation.
*/
typedef struct {
/*Ofs*/
......@@ -776,14 +804,14 @@ typedef struct {
* Recognize the difference by comparing the length of the resident attribute
* value. If it is 48, then the following fields are missing. If it is 72 then
* the fields are present. Maybe just check like this:
* if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) {
* Assume NTFS 1.2- format.
* If (volume version is 3.x)
* Upgrade attribute to NTFS 3.x format.
* else
* Use NTFS 1.2- format for access.
* } else
* Use NTFS 3.x format for access.
* if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) {
* Assume NTFS 1.2- format.
* If (volume version is 3.x)
* Upgrade attribute to NTFS 3.x format.
* else
* Use NTFS 1.2- format for access.
* } else
* Use NTFS 3.x format for access.
* Only problem is that it might be legal to set the length of the value to
* arbitrarily large values thus spoiling this check. - But chkdsk probably
* views that as a corruption, assuming that it behaves like this for all
......@@ -838,23 +866,23 @@ typedef struct {
* itself. The list is sorted: first by attribute type, second by attribute
* name (if present), third by instance number. The extents of one
* non-resident attribute (if present) immediately follow after the initial
* extent. They are ordered by lowest_vcn and have their instace set to zero.
* extent. They are ordered by lowest_vcn and have their instace set to zero.
* It is not allowed to have two attributes with all sorting keys equal.
* - Further restrictions:
* - If not resident, the vcn to lcn mapping array has to fit inside the
* base mft record.
* - The attribute list attribute value has a maximum size of 256kb. This
* is imposed by the Windows cache manager.
* - Further restrictions:
* - If not resident, the vcn to lcn mapping array has to fit inside the
* base mft record.
* - The attribute list attribute value has a maximum size of 256kb. This
* is imposed by the Windows cache manager.
* - Attribute lists are only used when the attributes of mft record do not
* fit inside the mft record despite all attributes (that can be made
* non-resident) having been made non-resident. This can happen e.g. when:
* - File has a large number of hard links (lots of file name
* attributes present).
* - The mapping pairs array of some non-resident attribute becomes so
* - File has a large number of hard links (lots of file name
* attributes present).
* - The mapping pairs array of some non-resident attribute becomes so
* large due to fragmentation that it overflows the mft record.
* - The security descriptor is very complex (not applicable to
* NTFS 3.0 volumes).
* - There are many named streams.
* - The security descriptor is very complex (not applicable to
* NTFS 3.0 volumes).
* - There are many named streams.
*/
typedef struct {
/*Ofs*/
......@@ -898,7 +926,7 @@ typedef struct {
*/
typedef enum {
FILE_NAME_POSIX = 0x00,
/* This is the largest namespace. It is case sensitive and
/* This is the largest namespace. It is case sensitive and
allows all Unicode characters except for: '\0' and '/'.
Beware that in WinNT/2k files which eg have the same name
except for their case will not be distinguished by the
......@@ -906,14 +934,14 @@ typedef enum {
both "filename" and "fileName" without warning. */
FILE_NAME_WIN32 = 0x01,
/* The standard WinNT/2k NTFS long filenames. Case insensitive.
All Unicode chars except: '\0', '"', '*', '/', ':', '<',
All Unicode chars except: '\0', '"', '*', '/', ':', '<',
'>', '?', '\' and '|'. Further, names cannot end with a '.'
or a space. */
FILE_NAME_DOS = 0x02,
/* The standard DOS filenames (8.3 format). Uppercase only.
All 8-bit characters greater space, except: '"', '*', '+',
',', '/', ':', ';', '<', '=', '>', '?' and '\'. */
FILE_NAME_WIN32_AND_DOS = 0x03,
FILE_NAME_WIN32_AND_DOS = 0x03,
/* 3 means that both the Win32 and the DOS filenames are
identical and hence have been saved in this single filename
record. */
......@@ -928,9 +956,9 @@ typedef enum {
* reality and the more up to date values are present in the standard
* information attribute.
* NOTE: There is conflicting information about the meaning of each of the time
* fields but the meaning as defined below has been verified to be
* correct by practical experimentation on Windows NT4 SP6a and is hence
* assumed to be the one and only correct interpretation.
* fields but the meaning as defined below has been verified to be
* correct by practical experimentation on Windows NT4 SP6a and is hence
* assumed to be the one and only correct interpretation.
*/
typedef struct {
/*hex ofs*/
......@@ -970,13 +998,13 @@ typedef struct {
} __attribute__ ((__packed__)) FILE_NAME_ATTR;
/*
* GUID structures store globally unique identifiers (GUID). A GUID is a
* GUID structures store globally unique identifiers (GUID). A GUID is a
* 128-bit value consisting of one group of eight hexadecimal digits, followed
* by three groups of four hexadecimal digits each, followed by one group of
* twelve hexadecimal digits. GUIDs are Microsoft's implementation of the
* distributed computing environment (DCE) universally unique identifier (UUID).
* Example of a GUID:
* 1F010768-5A73-BC91-0010A52216A7
* 1F010768-5A73-BC91-0010A52216A7
*/
typedef struct {
u32 data1; /* The first eight hexadecimal digits of the GUID. */
......@@ -1042,7 +1070,7 @@ typedef struct {
//typedef enum { /* SID string prefix. */
// SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */
// SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */
// SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */
// SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */
// SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */
// SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */
// SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */
......@@ -1051,7 +1079,7 @@ typedef struct {
/*
* These relative identifiers (RIDs) are used with the above identifier
* authorities to make up universal well-known SIDs.
*
*
* Note: The relative identifier (RID) refers to the portion of a SID, which
* identifies a user or group in relation to the authority that issued the SID.
* For example, the universal well-known SID Creator Owner ID (S-1-3-0) is
......@@ -1131,38 +1159,38 @@ typedef enum { /* Identifier authority. */
/*
* The universal well-known SIDs:
*
* NULL_SID S-1-0-0
* WORLD_SID S-1-1-0
* LOCAL_SID S-1-2-0
* CREATOR_OWNER_SID S-1-3-0
* CREATOR_GROUP_SID S-1-3-1
* CREATOR_OWNER_SERVER_SID S-1-3-2
* CREATOR_GROUP_SERVER_SID S-1-3-3
* NULL_SID S-1-0-0
* WORLD_SID S-1-1-0
* LOCAL_SID S-1-2-0
* CREATOR_OWNER_SID S-1-3-0
* CREATOR_GROUP_SID S-1-3-1
* CREATOR_OWNER_SERVER_SID S-1-3-2
* CREATOR_GROUP_SERVER_SID S-1-3-3
*
* (Non-unique IDs) S-1-4
* (Non-unique IDs) S-1-4
*
* NT well-known SIDs:
*
* NT_AUTHORITY_SID S-1-5
* DIALUP_SID S-1-5-1
*
* NETWORD_SID S-1-5-2
* BATCH_SID S-1-5-3
* INTERACTIVE_SID S-1-5-4
* SERVICE_SID S-1-5-6
* ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session)
* PROXY_SID S-1-5-8
* SERVER_LOGON_SID S-1-5-9 (aka domain controller account)
* SELF_SID S-1-5-10 (self RID)
* AUTHENTICATED_USER_SID S-1-5-11
* RESTRICTED_CODE_SID S-1-5-12 (running restricted code)
* TERMINAL_SERVER_SID S-1-5-13 (running on terminal server)
* NT_AUTHORITY_SID S-1-5
* DIALUP_SID S-1-5-1
*
* (Logon IDs) S-1-5-5-X-Y
* NETWORD_SID S-1-5-2
* BATCH_SID S-1-5-3
* INTERACTIVE_SID S-1-5-4
* SERVICE_SID S-1-5-6
* ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session)
* PROXY_SID S-1-5-8
* SERVER_LOGON_SID S-1-5-9 (aka domain controller account)
* SELF_SID S-1-5-10 (self RID)
* AUTHENTICATED_USER_SID S-1-5-11
* RESTRICTED_CODE_SID S-1-5-12 (running restricted code)
* TERMINAL_SERVER_SID S-1-5-13 (running on terminal server)
*
* (NT non-unique IDs) S-1-5-0x15-...
* (Logon IDs) S-1-5-5-X-Y
*
* (Built-in domain) S-1-5-0x20
* (NT non-unique IDs) S-1-5-0x15-...
*
* (Built-in domain) S-1-5-0x20
*/
/*
......@@ -1170,8 +1198,8 @@ typedef enum { /* Identifier authority. */
*/
typedef union {
struct {
u32 low; /* Low 32-bits. */
u16 high; /* High 16-bits. */
u32 low; /* Low 32-bits. */
u16 high; /* High 16-bits. */
} __attribute__ ((__packed__)) parts;
u8 value[6]; /* Value as individual bytes. */
} __attribute__ ((__packed__)) SID_IDENTIFIER_AUTHORITY;
......@@ -1179,26 +1207,26 @@ typedef union {
/*
* The SID structure is a variable-length structure used to uniquely identify
* users or groups. SID stands for security identifier.
*
*
* The standard textual representation of the SID is of the form:
* S-R-I-S-S...
* S-R-I-S-S...
* Where:
* - The first "S" is the literal character 'S' identifying the following
* digits as a SID.
* digits as a SID.
* - R is the revision level of the SID expressed as a sequence of digits
* either in decimal or hexadecimal (if the later, prefixed by "0x").
* - I is the 48-bit identifier_authority, expressed as digits as R above.
* - S... is one or more sub_authority values, expressed as digits as above.
*
*
* Example SID; the domain-relative SID of the local Administrators group on
* Windows NT/2k:
* S-1-5-32-544
* S-1-5-32-544
* This translates to a SID with:
* revision = 1,
* sub_authority_count = 2,
* identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY
* sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID
* sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS
* revision = 1,
* sub_authority_count = 2,
* identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY
* sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID
* sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS
*/
typedef struct {
u8 revision;
......@@ -1423,7 +1451,7 @@ typedef enum {
/*
* The generic mapping array. Used to denote the mapping of each generic
* access right to a specific access mask.
*
*
* FIXME: What exactly is this and what is it for? (AIA)
*/
typedef struct {
......@@ -1513,49 +1541,46 @@ typedef enum {
/*
* The security descriptor control flags (16-bit).
*
* SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the
* SID pointed to by the Owner field was provided by a
* defaulting mechanism rather than explicitly provided by the
* original provider of the security descriptor. This may
* affect the treatment of the SID with respect to inheritence
* of an owner.
*
* SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the
* SID in the Group field was provided by a defaulting mechanism
* rather than explicitly provided by the original provider of
* the security descriptor. This may affect the treatment of
* the SID with respect to inheritence of a primary group.
*
* SE_DACL_PRESENT - This boolean flag, when set, indicates that the
* security descriptor contains a discretionary ACL. If this
* flag is set and the Dacl field of the SECURITY_DESCRIPTOR is
* null, then a null ACL is explicitly being specified.
*
* SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the
* ACL pointed to by the Dacl field was provided by a defaulting
* mechanism rather than explicitly provided by the original
* provider of the security descriptor. This may affect the
* treatment of the ACL with respect to inheritence of an ACL.
* This flag is ignored if the DaclPresent flag is not set.
*
* SE_SACL_PRESENT - This boolean flag, when set, indicates that the
* security descriptor contains a system ACL pointed to by the
* Sacl field. If this flag is set and the Sacl field of the
* 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 rather than explicitly provided by the original
* provider of the security descriptor. This may affect the
* 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 security descriptor are contiguous in memory
* and all pointer fields are expressed as offsets from the
* beginning of the security descriptor.
* SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the SID
* pointed to by the Owner field was provided by a defaulting mechanism
* rather than explicitly provided by the original provider of the
* security descriptor. This may affect the treatment of the SID with
* respect to inheritence of an owner.
*
* SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the SID in
* the Group field was provided by a defaulting mechanism rather than
* explicitly provided by the original provider of the security
* descriptor. This may affect the treatment of the SID with respect to
* inheritence of a primary group.
*
* SE_DACL_PRESENT - This boolean flag, when set, indicates that the security
* descriptor contains a discretionary ACL. If this flag is set and the
* Dacl field of the SECURITY_DESCRIPTOR is null, then a null ACL is
* explicitly being specified.
*
* SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the ACL
* pointed to by the Dacl field was provided by a defaulting mechanism
* rather than explicitly provided by the original provider of the
* security descriptor. This may affect the treatment of the ACL with
* respect to inheritence of an ACL. This flag is ignored if the
* DaclPresent flag is not set.
*
* SE_SACL_PRESENT - This boolean flag, when set, indicates that the security
* descriptor contains a system ACL pointed to by the Sacl field. If this
* flag is set and the Sacl field of the 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
* rather than explicitly provided by the original provider of the
* security descriptor. This may affect the 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
* security descriptor are contiguous in memory and all pointer fields are
* expressed as offsets from the beginning of the security descriptor.
*/
typedef enum {
SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001),
......@@ -1593,7 +1618,7 @@ typedef struct {
SE_SACL_PRESENT is set in the control field. If
SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL
is specified. */
u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if
u32 dacl; /* Byte offset to a discretionary ACL. Only valid, if
SE_DACL_PRESENT is set in the control field. If
SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL
(unconditionally granting access) is specified. */
......@@ -1610,7 +1635,7 @@ typedef struct {
typedef struct {
u8 revision; /* Revision level of the security descriptor. */
u8 alignment;
SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of
SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of
the descriptor as well as the following fields. */
SID *owner; /* Points to a SID representing an object's owner. If
this is NULL, no owner SID is present in the
......@@ -1622,7 +1647,7 @@ typedef struct {
SE_SACL_PRESENT is set in the control field. If
SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL
is specified. */
ACL *dacl; /* Points to a discretionary ACL. Only valid, if
ACL *dacl; /* Points to a discretionary ACL. Only valid, if
SE_DACL_PRESENT is set in the control field. If
SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL
(unconditionally granting access) is specified. */
......@@ -1655,11 +1680,11 @@ typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR;
/*
* On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one
* referenced instance of each unique security descriptor is stored.
*
*
* FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It
* does, however, contain two indexes ($SDH and $SII) as well as a named data
* stream ($SDS).
*
*
* Every unique security descriptor is assigned a unique security identifier
* (security_id, not to be confused with a SID). The security_id is unique for
* the NTFS volume and is used as an index into the $SII index, which maps
......@@ -1674,12 +1699,12 @@ typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR;
* new security descriptor is already present in the $SDS data stream), if a
* matching hash is found, but the security descriptors do not match, the
* search in the $SDH index is continued, searching for a next matching hash.
*
*
* When a precise match is found, the security_id coresponding to the security
* descriptor in the $SDS attribute is read from the found $SDH index entry and
* is stored in the $STANDARD_INFORMATION attribute of the file/directory to
* which the security descriptor is being applied. The $STANDARD_INFORMATION
* attribute is present in all base mft records (i.e. in all files and
* attribute is present in all base mft records (i.e. in all files and
* directories).
*
* If a match is not found, the security descriptor is assigned a new unique
......@@ -1690,7 +1715,7 @@ typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR;
* Note: Entries are never deleted from FILE_Secure, even if nothing
* references an entry any more.
*/
/*
* This header precedes each security descriptor in the $SDS data stream.
* This is also the index entry data part of both the $SII and $SDH indexes.
......@@ -1727,7 +1752,7 @@ typedef struct {
/*
* The index entry key used in the $SII index. The collation type is
* COLLATION_NTOFS_ULONG.
* COLLATION_NTOFS_ULONG.
*/
typedef struct {
u32 security_id; /* The security_id assigned to the descriptor. */
......@@ -1745,7 +1770,7 @@ typedef struct {
/*
* Attribute: Volume name (0x60).
*
*
* NOTE: Always resident.
* NOTE: Present only in FILE_Volume.
*/
......@@ -1759,7 +1784,7 @@ typedef struct {
typedef enum {
VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001),
VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002),
VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004),
VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004),
VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008),
VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010),
VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020),
......@@ -1773,7 +1798,7 @@ typedef enum {
* NOTE: Always resident.
* NOTE: Present only in FILE_Volume.
* NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses
* NTFS 1.2. I haven't personally seen other values yet.
* NTFS 1.2. I haven't personally seen other values yet.
*/
typedef struct {
u64 reserved; /* Not used (yet?). */
......@@ -1809,7 +1834,7 @@ typedef enum {
* allocation attribute:
*/
LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more
nodes branching off it. */
nodes branching off it. */
INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a
leaf node. */
NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */
......@@ -1876,7 +1901,7 @@ typedef struct {
the index allocation attribute), when
an index block is >= than a cluster,
otherwise this will be the log of
the size (like how the encoding of
the size (like how the encoding of
the mft record size and the index
record size found in the boot sector
work). Has to be a power of 2. */
......@@ -1889,7 +1914,7 @@ typedef struct {
* Attribute: Index allocation (0xa0).
*
* NOTE: Always non-resident (doesn't make sense to be resident anyway!).
*
*
* This is an array of index blocks. Each index block starts with an
* INDEX_BLOCK structure containing an index header, followed by a sequence of
* index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER.
......@@ -1967,7 +1992,7 @@ typedef enum {
*
* The $Q index contains one entry for each existing user_id on the volume. The
* index key is the user_id of the user/group owning this quota control entry,
* i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the
* i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the
* owner_id, is found in the standard information attribute. The collation rule
* for $Q is COLLATION_NTOFS_ULONG.
*
......@@ -2143,9 +2168,9 @@ typedef struct {
* They are defined as follows:
* bit 29: Name surrogate bit. If set, the filename is an alias for
* another object in the system.
* bit 30: High-latency bit. If set, accessing the first byte of data will
* bit 30: High-latency bit. If set, accessing the first byte of data will
* be slow. (E.g. the data is stored on a tape drive.)
* bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User
* bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User
* defined tags have to use zero here.
*/
typedef enum {
......@@ -2211,8 +2236,8 @@ typedef enum {
* Attribute: Extended attribute (EA) (0xe0).
*
* NOTE: Always non-resident. (Is this true?)
*
* Like the attribute list and the index buffer list, the EA attribute value is
*
* Like the attribute list and the index buffer list, the EA attribute value is
* a sequence of EA_ATTR variable length records.
*
* FIXME: It appears weird that the EA name is not unicode. Is it true?
......
/*
* 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.
*
* 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
* 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
* 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
* 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
*/
......@@ -38,7 +38,7 @@ static inline void *ntfs_malloc_nofs(unsigned long size)
{
if (likely(size <= PAGE_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 (void *)__get_free_page(GFP_NOFS |
__GFP_HIGHMEM); */
......@@ -61,4 +61,3 @@ static inline void ntfs_free(void *addr)
}
#endif /* _LINUX_NTFS_MALLOC_H */
/*
* 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
* 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
* 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
* 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
*/
......@@ -26,7 +26,7 @@
* post_read_mst_fixup - deprotect multi sector transfer protected data
* @b: pointer to the data to deprotect
* @size: size in bytes of @b
*
*
* Perform the necessary post read multi sector transfer fixup and detect the
* presence of incomplete multi sector transfers. - In that case, overwrite the
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
......@@ -54,13 +54,13 @@ int post_read_mst_fixup(NTFS_RECORD *b, const u32 size)
usa_ofs + (usa_count * 2) > size ||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count)
return 0;
/* Position of usn in update sequence array. */
/* Position of usn in update sequence array. */
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
/*
/*
* The update sequence number which has to be equal to each of the
* u16 values before they are fixed up. Note no need to care for
* endianness since we are comparing and moving data for on disk
* structures which means the data is consistent. - If it is
* structures which means the data is consistent. - If it is
* consistenty the wrong endianness it doesn't make any difference.
*/
usn = *usa_pos;
......@@ -68,18 +68,18 @@ int post_read_mst_fixup(NTFS_RECORD *b, const u32 size)
* Position in protected data of first u16 that needs fixing up.
*/
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/*
/*
* Check for incomplete multi sector transfer(s).
*/
while (usa_count--) {
if (*data_pos != usn) {
if (*data_pos != usn) {
/*
* Incomplete multi sector transfer detected! )-:
* Set the magic to "BAAD" and return failure.
* Note that magic_BAAD is already converted to le32.
*/
b->magic = magic_BAAD;
return -EINVAL;
return -EINVAL;
}
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
......@@ -93,9 +93,9 @@ int post_read_mst_fixup(NTFS_RECORD *b, const u32 size)
* the usa into the data buffer.
*/
*data_pos = *(++usa_pos);
/* Increment position in data as well. */
/* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
}
return 0;
}
......@@ -103,7 +103,7 @@ int post_read_mst_fixup(NTFS_RECORD *b, const u32 size)
* pre_write_mst_fixup - apply multi sector transfer protection
* @b: pointer to the data to protect
* @size: size in bytes of @b
*
*
* Perform the necessary pre write multi sector transfer fixup on the data
* pointer to by @b of @size.
*
......@@ -126,7 +126,8 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
u16 *usa_pos, *data_pos;
/* 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;
/* Setup the variables. */
usa_ofs = le16_to_cpu(b->usa_ofs);
......@@ -138,10 +139,10 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
usa_ofs + (usa_count * 2) > size ||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count)
return -EINVAL;
/* Position of usn in update sequence array. */
/* Position of usn in update sequence array. */
usa_pos = (u16*)((u8*)b + usa_ofs);
/*
* Cyclically increment the update sequence number
* Cyclically increment the update sequence number
* (skipping 0 and -1, i.e. 0xffff).
*/
usn = le16_to_cpup(usa_pos) + 1;
......@@ -151,10 +152,10 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
*usa_pos = usn;
/* Position in data of first u16 that needs fixing up. */
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/* Fixup all sectors. */
while (usa_count--) {
/* Fixup all sectors. */
while (usa_count--) {
/*
* Increment the position in the usa and save the
* Increment the position in the usa and save the
* original data from the data buffer into the usa.
*/
*(++usa_pos) = *data_pos;
......@@ -162,14 +163,14 @@ int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
*data_pos = usn;
/* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
}
return 0;
}
/**
* post_write_mst_fixup - fast deprotect multi sector transfer protected data
* @b: pointer to the data to deprotect
*
*
* Perform the necessary post write multi sector transfer fixup, not checking
* for any errors, because we assume we have just used pre_write_mst_fixup(),
* thus the data will be fine or we would never have gotten here.
......@@ -181,13 +182,13 @@ void post_write_mst_fixup(NTFS_RECORD *b)
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
u16 usa_count = le16_to_cpu(b->usa_count) - 1;
/* Position of usn in update sequence array. */
/* Position of usn in update sequence array. */
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
/* Position in protected data of first u16 that needs fixing up. */
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
/* Fixup all sectors. */
/* Fixup all sectors. */
while (usa_count--) {
/*
* Increment position in usa and restore original data from
......@@ -195,8 +196,7 @@ void post_write_mst_fixup(NTFS_RECORD *b)
*/
*data_pos = *(++usa_pos);
/* Increment position in data as well. */
/* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
}
}
}
......@@ -10,13 +10,13 @@
* 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
* 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
* 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
*/
......@@ -77,10 +77,10 @@ extern struct inode_operations ntfs_empty_inode_ops;
/* Generic macros to convert pointers to values and vice versa. */
#ifndef p2n
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
#endif
#ifndef n2p
#define n2p(p) ((ptrdiff_t*)((ptrdiff_t)(p)))
#define n2p(p) ((ptrdiff_t*)((ptrdiff_t)(p)))
#endif
/**
......
......@@ -31,6 +31,7 @@
#include "ntfs.h"
#include "sysctl.h"
#include "logfile.h"
/* Number of mounted file systems which have compression enabled. */
static unsigned long ntfs_nr_compression_users;
......@@ -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 (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.");
return -EROFS;
}
......@@ -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))
iput(tmp_ino);
ntfs_error(vol->sb, "Failed to load $MFTMirr.");
/* Caller will display error message. */
return FALSE;
}
/*
......@@ -773,6 +774,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
run_list_element *rl, rl2[2];
int mrecs_per_page, i;
ntfs_debug("Entering.");
/* Compare contents of $MFT and $MFTMirr. */
mrecs_per_page = PAGE_CACHE_SIZE / vol->mft_record_size;
BUG_ON(!mrecs_per_page);
......@@ -808,7 +810,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
++index;
}
/* Make sure the record is ok. */
if (is_baad_recordp(kmft)) {
if (ntfs_is_baad_recordp(kmft)) {
ntfs_error(sb, "Incomplete multi sector transfer "
"detected in mft record %i.", i);
mm_unmap_out:
......@@ -817,7 +819,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
ntfs_unmap_page(mft_page);
return FALSE;
}
if (is_baad_recordp(kmirr)) {
if (ntfs_is_baad_recordp(kmirr)) {
ntfs_error(sb, "Incomplete multi sector transfer "
"detected in mft mirror record %i.", i);
goto mm_unmap_out;
......@@ -832,7 +834,7 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
/* Compare the two records. */
if (memcmp(kmft, kmirr, bytes)) {
ntfs_error(sb, "$MFT and $MFTMirr (record %i) do not "
"match. Run ntfsfix or chkdsk.", i);
"match. Run ntfsfix or chkdsk.", i);
goto mm_unmap_out;
}
kmft += vol->mft_record_size;
......@@ -862,13 +864,42 @@ static BOOL check_mft_mirror(ntfs_volume *vol)
do {
if (rl2[i].vcn != rl[i].vcn || rl2[i].lcn != rl[i].lcn ||
rl2[i].length != rl[i].length) {
ntfs_error(sb, "$MFTMirr location mismatch. "
ntfs_error(sb, "$MFTMirr location mismatch. "
"Run chkdsk.");
up_read(&mirr_ni->run_list.lock);
return FALSE;
}
} while (rl2[i++].length);
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;
}
......@@ -926,7 +957,7 @@ static BOOL load_and_init_upcase(ntfs_volume *vol)
goto read_partial_upcase_page;
}
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));
iput(ino);
down(&ntfs_lock);
......@@ -1094,25 +1125,42 @@ static BOOL load_system_files(ntfs_volume *vol)
unmap_mft_record(NTFS_I(vol->vol_ino));
printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver,
vol->minor_ver);
#ifdef NTFS_RW
/*
* Get the inode for the logfile and empty it if this is a read-write
* mount.
* Get the inode for the logfile, check it and determine if the volume
* was shutdown cleanly.
*/
// TODO: vol->logfile_ino = ;
// TODO: Cleanup for error case at end of function.
tmp_ino = ntfs_iget(sb, FILE_LogFile);
if (IS_ERR(tmp_ino) || is_bad_inode(tmp_ino)) {
if (!IS_ERR(tmp_ino))
iput(tmp_ino);
ntfs_error(sb, "Failed to load $LogFile.");
// FIMXE: We only want to empty the thing so pointless bailing
// out. Can recover/ignore.
goto iput_vol_err_out;
if (!load_and_check_logfile(vol) ||
!ntfs_is_logfile_clean(vol->logfile_ino)) {
static const char *es1 = "Failed to load $LogFile";
static const char *es2 = "$LogFile is not clean";
static const char *es3 = ". Mount in Windows.";
/* If a read-write mount, convert it to a read-only mount. */
if (!(sb->s_flags & MS_RDONLY)) {
if (!(vol->on_errors & (ON_ERRORS_REMOUNT_RO |
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: 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.
iput(tmp_ino);
#endif
/*
* Get the inode for the attribute definitions file and parse the
* attribute definitions.
......@@ -1122,7 +1170,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(tmp_ino))
iput(tmp_ino);
ntfs_error(sb, "Failed to load $AttrDef.");
goto iput_vol_err_out;
goto iput_logfile_err_out;
}
// FIXME: Parse the attribute definitions.
iput(tmp_ino);
......@@ -1132,7 +1180,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(vol->root_ino))
iput(vol->root_ino);
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 (vol->major_ver < 3)
......@@ -1166,7 +1214,11 @@ static BOOL load_system_files(ntfs_volume *vol)
iput(vol->secure_ino);
iput_root_err_out:
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_lcnbmp_err_out:
iput(vol->lcnbmp_ino);
......
......@@ -2,20 +2,20 @@
* types.h - Defines for NTFS Linux kernel driver specific types.
* 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
* 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
* 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
* 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
*/
......@@ -23,14 +23,6 @@
#ifndef _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. */
typedef u16 uchar_t;
#define UCHAR_T_SIZE_BITS 1
......@@ -42,15 +34,22 @@ typedef u16 uchar_t;
typedef s64 VCN;
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
* @vcn: starting vcn of the current array element
* @lcn: starting lcn of the current array element
* @length: length in clusters of the current array element
*
*
* The last vcn (in fact the last vcn + 1) is reached when length == 0.
*
* When lcn == -1 this means that the count vcns starting at vcn are not
*
* When lcn == -1 this means that the count vcns starting at vcn are not
* physically allocated (i.e. this is a hole / data is sparse).
*/
typedef struct { /* In memory vcn to lcn mapping structure element. */
......@@ -63,7 +62,7 @@ typedef struct { /* In memory vcn to lcn mapping structure element. */
* run_list - in memory vcn to lcn mapping array including a read/write lock
* @rl: pointer to an array of run list elements
* @lock: read/write spinlock for serializing access to @rl
*
*
*/
typedef struct {
run_list_element *rl;
......@@ -81,4 +80,3 @@ typedef enum {
} IGNORE_CASE_BOOL;
#endif /* _LINUX_NTFS_TYPES_H */
......@@ -116,6 +116,7 @@ typedef enum {
create filenames in the POSIX namespace.
Otherwise be case insensitive and create
file names in WIN32 namespace. */
NV_LogFileEmpty, /* 1: $LogFile journal is empty. */
} ntfs_volume_flags;
/*
......@@ -140,5 +141,6 @@ static inline void NVolClear##flag(ntfs_volume *vol) \
NVOL_FNS(Errors)
NVOL_FNS(ShowSystemFiles)
NVOL_FNS(CaseSensitive)
NVOL_FNS(LogFileEmpty)
#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