From 1be3d9fe6608663b17228bb62d91fbeebf5f7a3d Mon Sep 17 00:00:00 2001 From: "(none)!yura" <(none)!yura> Date: Thu, 13 Jan 2005 15:55:15 +0000 Subject: [PATCH] Update logfile.h to be sync with kernel. Update ntfsdump_logfile accordingly. (Logical change 1.656) --- include/ntfs/logfile.h | 285 +++++++++++++++++++++++------------ ntfsprogs/ntfsdump_logfile.c | 27 ++-- 2 files changed, 206 insertions(+), 106 deletions(-) diff --git a/include/ntfs/logfile.h b/include/ntfs/logfile.h index d5144d64..8ecf18e9 100644 --- a/include/ntfs/logfile.h +++ b/include/ntfs/logfile.h @@ -27,17 +27,19 @@ #include "layout.h" /* - * Log file organization: + * Journal ($LogFile) organization: * * Two restart areas present in the first two pages (restart pages, one restart - * area in each page). When the volume is unmounted they should be identical. + * 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 record - * header going up to log file size. Not all pages contain log records when a + * 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 + * 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 @@ -49,6 +51,11 @@ * 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). */ @@ -56,112 +63,192 @@ 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. +/* 4*/ le16 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. */ +/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */ -/* 8*/ LSN chkdsk_lsn; /* The last log file sequence number found by +/* 8*/ leLSN 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 +/* 16*/ le32 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_offset. */ -/* 20*/ u32 log_page_size; /* Byte size of log file pages, has to be >= - 512 and a power of 2. Usually is 4096 (or - is it just set to system_page_size?). */ -/* 24*/ u16 restart_offset; /* Byte offset from the start of this header to + value of the restart_area_offset. */ +/* 20*/ le32 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*/ le16 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 +/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major version is 1. */ -/* 28*/ s16 major_ver; /* Log file major version. We only support +/* 28*/ sle16 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. + */ +enum { + RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002), + RESTART_SPACE_FILLER = 0xffff, /* gcc: Force enum bit width to 16. */ +} __attribute__ ((__packed__)); + +typedef le16 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_offset value found in - * it. See notes at restart_offset above. + * 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 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? */ -/* 8*/ u16 log_clients; /* Number of log client records in the array of +/* 0*/ leLSN 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*/ le16 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. 0xffff - means that there are no free log client - records in the array. If != 0xffff, check - that log_clients > client_free_list. On a - clean volume this is != 0xffff, should at - present always be 0. At present on dirty - volume this is 0xffff. */ -/* 12*/ u16 client_in_use_list; /* The index of the first in-use log client +/* 10*/ le16 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*/ le16 client_in_use_list;/* The index of the first in-use log client record in the array of log client records. - 0xffff means that there are no in-use log - client records in the array. If != 0xffff - check that log_clients > client_in_use_list. - On a clean volume this is 0xffff. On a - dirty volume this is != 0xffff. At present - on dirty volume this is 0. */ -/* 14*/ u16 flags; /* Flags modifying LFS behaviour. = 0 */ -/* 16*/ u32 seq_number_bits; /* How many bits to use for the sequence - number. I have seen 0x2c and 0x2d. */ -/* 20*/ u16 restart_area_length;/* Length of the restart area. Following - checks required if version matches. - Otherwise, skip them. restart_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 + 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*/ le32 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*/ le16 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*/ le16 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, the offset is otherwise 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_offset - + client_array_offset has to be <= 510. + 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. */ -/* 24*/ s64 file_size; /* Byte size of the log file. If the - restart_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! If the structure is deprotected - then these checks are futile of course. - Calculate the file_size bits and check that - seq_number_bits == 0x43 - file_size bits. - = 0x400000 */ -/* 32*/ u32 last_lsn_data_length;/* ??? = 0, 0x40 */ -/* 36*/ u16 record_length; /* Byte size of log records. If the version - matches then check that the value of - record_length is a multiple of 8, i.e. - (record_length + 7) & ~7 == record_length. - = 0x30 */ -/* 38*/ u16 log_page_data_offset;/* ??? = 0x40 */ -/* 40*/ u32 unknown; /* ??? It seems like it = 0 if clean and it != 0 - if dirty. */ -/* 44*/ u32 reserved; /* Reserved/alignment to 8-byte boundary. */ + 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*/ sle64 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*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including + the log record header. On create set to + 0. */ +/* 36*/ le16 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*/ le16 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*/ le32 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*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */ /* sizeof() = 48 (0x30) bytes */ } __attribute__ ((__packed__)) RESTART_AREA; @@ -171,26 +258,36 @@ typedef struct { */ typedef struct { /*Ofs*/ -/* 0*/ LSN oldest_lsn; /* Oldest LSN needed by this client. */ -/* 8*/ LSN client_restart_lsn; /* LSN at which this client needs to restart +/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create + set to 0. */ +/* 8*/ leLSN 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. */ -/* 16*/ u16 prev_client; /* The offset to the previous log client record - in the array of log client records. 0xffff - means there is no previous client record, - i.e. this is the first one. */ -/* 18*/ u16 next_client; /* The offset to the next log client record in - the array of log client records. 0xffff - means there are no next client records, i.e. - this is the last one. */ -/* 20*/ u16 seq_number; /* ??? It is 1 when clean and 0 when dirty, - but don't know if this is always the case. */ + of the time. At create set to 0. */ +/* 16*/ le16 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*/ le16 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*/ le16 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. = 8 */ -/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. = NTFS */ +/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should + always be 8. */ +/* 32*/ ntfschar 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; diff --git a/ntfsprogs/ntfsdump_logfile.c b/ntfsprogs/ntfsdump_logfile.c index 379791db..2c52b6b3 100644 --- a/ntfsprogs/ntfsdump_logfile.c +++ b/ntfsprogs/ntfsdump_logfile.c @@ -309,13 +309,13 @@ static void restart_header_sanity(RESTART_PAGE_HEADER *rstr, u8 *buf) "corrupt: Update sequence array overlaps or " "is behind first protected sequence number. " "Cannot handle this yet.\n"); - if (usa_end_ofs > le16_to_cpu(rstr->restart_offset)) + if (usa_end_ofs > le16_to_cpu(rstr->restart_area_offset)) log_err_exit(buf, "Restart page header in $LogFile is " "corrupt: Update sequence array overlaps or " "is behind restart area. Cannot handle this " "yet.\n"); /* Finally, verify the offset of the restart area. */ - if (le16_to_cpu(rstr->restart_offset) & 7) + if (le16_to_cpu(rstr->restart_area_offset) & 7) log_err_exit(buf, "Restart page header in $LogFile is " "corrupt: Restart area offset is not aligned " "to 8-byte boundary. Cannot handle this " @@ -341,8 +341,8 @@ static void dump_restart_areas_header(RESTART_PAGE_HEADER *rstr) (unsigned int)le32_to_cpu(rstr->log_page_size), (unsigned int)le32_to_cpu(rstr->log_page_size)); printf("restart_offset = %u (0x%x)\n", - le16_to_cpu(rstr->restart_offset), - le16_to_cpu(rstr->restart_offset)); + le16_to_cpu(rstr->restart_area_offset), + le16_to_cpu(rstr->restart_area_offset)); } static void dump_restart_areas_area(RESTART_PAGE_HEADER *rstr) @@ -351,7 +351,8 @@ static void dump_restart_areas_area(RESTART_PAGE_HEADER *rstr) RESTART_AREA *ra; int client; - ra = (RESTART_AREA*)((u8*)rstr + le16_to_cpu(rstr->restart_offset)); + ra = (RESTART_AREA*)((u8*)rstr + + le16_to_cpu(rstr->restart_area_offset)); printf("current_lsn = %lli (0x%llx)\n", (long long)sle64_to_cpu(ra->current_lsn), (unsigned long long)sle64_to_cpu(ra->current_lsn)); @@ -379,13 +380,15 @@ static void dump_restart_areas_area(RESTART_PAGE_HEADER *rstr) printf("last_lsn_data_length = %u (0x%x)\n", (unsigned int)le32_to_cpu(ra->last_lsn_data_length), (unsigned int)le32_to_cpu(ra->last_lsn_data_length)); - printf("record_length = %u (0x%x)\n", le16_to_cpu(ra->record_length), - le16_to_cpu(ra->record_length)); + printf("log_record_header_length = %u (0x%x)\n", + le16_to_cpu(ra->log_record_header_length), + le16_to_cpu(ra->log_record_header_length)); printf("log_page_data_offset = %u (0x%x)\n", le16_to_cpu(ra->log_page_data_offset), le16_to_cpu(ra->log_page_data_offset)); - printf("unknown = %u (0x%x)\n", le16_to_cpu(ra->unknown), - le16_to_cpu(ra->unknown)); + printf("restart_log_open_count = %u (0x%x)\n", + le32_to_cpu(ra->restart_log_open_count), + le32_to_cpu(ra->restart_log_open_count)); lcr = (LOG_CLIENT_RECORD*)((u8*)ra + le16_to_cpu(ra->client_array_offset)); for (client = 0; client < le16_to_cpu(ra->log_clients); client++) { @@ -467,11 +470,11 @@ rstr_pass_loc: /* Exclude the usa from the comparison. */ ra = (RESTART_AREA*)((u8*)rstr1 + - le16_to_cpu(rstr1->restart_offset)); + le16_to_cpu(rstr1->restart_area_offset)); if (!memcmp(rstr1, rstr, le16_to_cpu(rstr1->usa_ofs)) && !memcmp((u8*)rstr1 + le16_to_cpu( - rstr1->restart_offset), (u8*)rstr + - le16_to_cpu(rstr->restart_offset), + rstr1->restart_area_offset), (u8*)rstr + + le16_to_cpu(rstr->restart_area_offset), le16_to_cpu(ra->restart_area_length))) { puts("\nSkipping analysis of second restart page " "because it fully matches the first "