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.
- 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.
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
* 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
*/
/*
* 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.
*/
/*
* 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
* 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 +
} 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 {
/*
* 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.
*
* 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
/* 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
* 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),
* 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
} __attribute__ ((__packed__)) ATTR_DEF;
/*
- * Attribute flags (16-bit).
+ * Attribute flags (16-bit).
*/
typedef enum {
ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001),
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 {
/* 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. */
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),
* 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*/
* 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
* 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*/
*/
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
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. */
* 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*/
} __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. */
//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 */
/*
* 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
/*
* 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
*/
/*
*/
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;
/*
* 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;
/*
* 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 {
/*
* 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),
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. */
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
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. */
/*
* 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
* 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
* 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.
/*
* 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. */
/*
* Attribute: Volume name (0x60).
- *
+ *
* NOTE: Always resident.
* NOTE: Present only in FILE_Volume.
*/
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),
* 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?). */
* 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. */
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. */
* 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.
*
* 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.
*
* 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 {
* 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?
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
*/
{
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); */
}
#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
*/
* 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!)
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;
* 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);
}
* 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;
}
* 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.
*
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);
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;
*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;
*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.
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
*/
*data_pos = *(++usa_pos);
- /* Increment position in data as well. */
+ /* Increment position in data as well. */
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
- }
+ }
}
-
* 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
*/
/* 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
/**
#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;
*/
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;
}
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;
}
/*
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);
++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:
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;
/* 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;
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;
}
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);
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.
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);
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)
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);
* 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
*/
#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
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. */
* 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;
} IGNORE_CASE_BOOL;
#endif /* _LINUX_NTFS_TYPES_H */
-
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;
/*
NVOL_FNS(Errors)
NVOL_FNS(ShowSystemFiles)
NVOL_FNS(CaseSensitive)
+NVOL_FNS(LogFileEmpty)
#endif /* _LINUX_NTFS_VOLUME_H */