]> git.hungrycats.org Git - linux/commitdiff
NTFS: Read the journal ($LogFile) and determine if the volume has been shutdown cleanly
authorAnton Altaparmakov <aia21@cantab.net>
Fri, 7 May 2004 15:18:37 +0000 (16:18 +0100)
committerAnton Altaparmakov <aia21@cantab.net>
Fri, 7 May 2004 15:18:37 +0000 (16:18 +0100)
      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.

fs/ntfs/ChangeLog
fs/ntfs/Makefile
fs/ntfs/layout.h
fs/ntfs/logfile.c [new file with mode: 0644]
fs/ntfs/logfile.h [new file with mode: 0644]
fs/ntfs/malloc.h
fs/ntfs/mst.c
fs/ntfs/ntfs.h
fs/ntfs/super.c
fs/ntfs/types.h
fs/ntfs/volume.h

index 00aec932049d5a3ad2201122788cdf42acc8995c..0e3e151ac19cf13d457560efca7dc8cbec93a06c 100644 (file)
@@ -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.
 
index c79000e79e0e4c49f3e8548ad0dc74ad12edf664..2b9f449b54953c833c6ad478e3ba8adf6a1f4bb2 100644 (file)
@@ -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
index 9016e48245d49e9ec976b34fcb24e149cae94627..052610ac6cbfa0544ec442d5b91e5246fb42d458 100644 (file)
  * 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.
  */
 
 /*
@@ -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?
diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c
new file mode 100644 (file)
index 0000000..868d720
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+ * 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 */
diff --git a/fs/ntfs/logfile.h b/fs/ntfs/logfile.h
new file mode 100644 (file)
index 0000000..4ebec5e
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * 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 */
index 98170fb3773fc9f8fc6d541f6468d75a059bf681..af1a1cbfe41c357dcf81f75dd8c72e93569f5438 100644 (file)
@@ -1,20 +1,20 @@
 /*
  * 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 */
-
index 00f63c1f35247cd9b40d7b6ad826ca92e62ae0b4..5ef2b1ef19c27ea10aec87704814a4d69707576f 100644 (file)
@@ -1,21 +1,21 @@
 /*
  * 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);
-        }
+       }
 }
-
index 90bae14c6dc54f248b8652e1443f79cf748132af..82f17d6960461ddbbc67af5e4c02098e7a45ce8e 100644 (file)
  * 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
 
 /**
index 0fcd6c70d9ad9489c97e959633dd7ba6ab9d732c..973be10d97650c02ef68f6a52bb2a2474e9c578e 100644 (file)
@@ -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 @@ mft_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;
@@ -832,7 +834,7 @@ mft_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;
@@ -862,13 +864,42 @@ mft_unmap_out:
        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 @@ read_partial_upcase_page:
                        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 @@ get_ctx_vol_failed:
        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 @@ get_ctx_vol_failed:
                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 @@ get_ctx_vol_failed:
                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 @@ iput_sec_err_out:
        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);
index 12c80e32390951a0af539769539a249402914c6b..1e5853e5a2d3f8e3851bbd2e7dc49fd150d3c85f 100644 (file)
@@ -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
  */
 #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 */
-
index 92ec6b407b8513f89bb91b183f9e3460003abf5a..e5a237357a564eca61b430c331b443c5d8621d39 100644 (file)
@@ -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 */