162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/blkdev.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/random.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "debug.h"
1462306a36Sopenharmony_ci#include "ntfs.h"
1562306a36Sopenharmony_ci#include "ntfs_fs.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * LOG FILE structs
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci// clang-format off
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define MaxLogFileSize     0x100000000ull
2462306a36Sopenharmony_ci#define DefaultLogPageSize 4096
2562306a36Sopenharmony_ci#define MinLogRecordPages  0x30
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct RESTART_HDR {
2862306a36Sopenharmony_ci	struct NTFS_RECORD_HEADER rhdr; // 'RSTR'
2962306a36Sopenharmony_ci	__le32 sys_page_size; // 0x10: Page size of the system which initialized the log.
3062306a36Sopenharmony_ci	__le32 page_size;     // 0x14: Log page size used for this log file.
3162306a36Sopenharmony_ci	__le16 ra_off;        // 0x18:
3262306a36Sopenharmony_ci	__le16 minor_ver;     // 0x1A:
3362306a36Sopenharmony_ci	__le16 major_ver;     // 0x1C:
3462306a36Sopenharmony_ci	__le16 fixups[];
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define LFS_NO_CLIENT 0xffff
3862306a36Sopenharmony_ci#define LFS_NO_CLIENT_LE cpu_to_le16(0xffff)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct CLIENT_REC {
4162306a36Sopenharmony_ci	__le64 oldest_lsn;
4262306a36Sopenharmony_ci	__le64 restart_lsn; // 0x08:
4362306a36Sopenharmony_ci	__le16 prev_client; // 0x10:
4462306a36Sopenharmony_ci	__le16 next_client; // 0x12:
4562306a36Sopenharmony_ci	__le16 seq_num;     // 0x14:
4662306a36Sopenharmony_ci	u8 align[6];        // 0x16:
4762306a36Sopenharmony_ci	__le32 name_bytes;  // 0x1C: In bytes.
4862306a36Sopenharmony_ci	__le16 name[32];    // 0x20: Name of client.
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic_assert(sizeof(struct CLIENT_REC) == 0x60);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Two copies of these will exist at the beginning of the log file */
5462306a36Sopenharmony_cistruct RESTART_AREA {
5562306a36Sopenharmony_ci	__le64 current_lsn;    // 0x00: Current logical end of log file.
5662306a36Sopenharmony_ci	__le16 log_clients;    // 0x08: Maximum number of clients.
5762306a36Sopenharmony_ci	__le16 client_idx[2];  // 0x0A: Free/use index into the client record arrays.
5862306a36Sopenharmony_ci	__le16 flags;          // 0x0E: See RESTART_SINGLE_PAGE_IO.
5962306a36Sopenharmony_ci	__le32 seq_num_bits;   // 0x10: The number of bits in sequence number.
6062306a36Sopenharmony_ci	__le16 ra_len;         // 0x14:
6162306a36Sopenharmony_ci	__le16 client_off;     // 0x16:
6262306a36Sopenharmony_ci	__le64 l_size;         // 0x18: Usable log file size.
6362306a36Sopenharmony_ci	__le32 last_lsn_data_len; // 0x20:
6462306a36Sopenharmony_ci	__le16 rec_hdr_len;    // 0x24: Log page data offset.
6562306a36Sopenharmony_ci	__le16 data_off;       // 0x26: Log page data length.
6662306a36Sopenharmony_ci	__le32 open_log_count; // 0x28:
6762306a36Sopenharmony_ci	__le32 align[5];       // 0x2C:
6862306a36Sopenharmony_ci	struct CLIENT_REC clients[]; // 0x40:
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistruct LOG_REC_HDR {
7262306a36Sopenharmony_ci	__le16 redo_op;      // 0x00:  NTFS_LOG_OPERATION
7362306a36Sopenharmony_ci	__le16 undo_op;      // 0x02:  NTFS_LOG_OPERATION
7462306a36Sopenharmony_ci	__le16 redo_off;     // 0x04:  Offset to Redo record.
7562306a36Sopenharmony_ci	__le16 redo_len;     // 0x06:  Redo length.
7662306a36Sopenharmony_ci	__le16 undo_off;     // 0x08:  Offset to Undo record.
7762306a36Sopenharmony_ci	__le16 undo_len;     // 0x0A:  Undo length.
7862306a36Sopenharmony_ci	__le16 target_attr;  // 0x0C:
7962306a36Sopenharmony_ci	__le16 lcns_follow;  // 0x0E:
8062306a36Sopenharmony_ci	__le16 record_off;   // 0x10:
8162306a36Sopenharmony_ci	__le16 attr_off;     // 0x12:
8262306a36Sopenharmony_ci	__le16 cluster_off;  // 0x14:
8362306a36Sopenharmony_ci	__le16 reserved;     // 0x16:
8462306a36Sopenharmony_ci	__le64 target_vcn;   // 0x18:
8562306a36Sopenharmony_ci	__le64 page_lcns[];  // 0x20:
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic_assert(sizeof(struct LOG_REC_HDR) == 0x20);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define RESTART_ENTRY_ALLOCATED    0xFFFFFFFF
9162306a36Sopenharmony_ci#define RESTART_ENTRY_ALLOCATED_LE cpu_to_le32(0xFFFFFFFF)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct RESTART_TABLE {
9462306a36Sopenharmony_ci	__le16 size;       // 0x00: In bytes
9562306a36Sopenharmony_ci	__le16 used;       // 0x02: Entries
9662306a36Sopenharmony_ci	__le16 total;      // 0x04: Entries
9762306a36Sopenharmony_ci	__le16 res[3];     // 0x06:
9862306a36Sopenharmony_ci	__le32 free_goal;  // 0x0C:
9962306a36Sopenharmony_ci	__le32 first_free; // 0x10:
10062306a36Sopenharmony_ci	__le32 last_free;  // 0x14:
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic_assert(sizeof(struct RESTART_TABLE) == 0x18);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct ATTR_NAME_ENTRY {
10762306a36Sopenharmony_ci	__le16 off; // Offset in the Open attribute Table.
10862306a36Sopenharmony_ci	__le16 name_bytes;
10962306a36Sopenharmony_ci	__le16 name[];
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistruct OPEN_ATTR_ENRTY {
11362306a36Sopenharmony_ci	__le32 next;            // 0x00: RESTART_ENTRY_ALLOCATED if allocated
11462306a36Sopenharmony_ci	__le32 bytes_per_index; // 0x04:
11562306a36Sopenharmony_ci	enum ATTR_TYPE type;    // 0x08:
11662306a36Sopenharmony_ci	u8 is_dirty_pages;      // 0x0C:
11762306a36Sopenharmony_ci	u8 is_attr_name;        // 0x0B: Faked field to manage 'ptr'
11862306a36Sopenharmony_ci	u8 name_len;            // 0x0C: Faked field to manage 'ptr'
11962306a36Sopenharmony_ci	u8 res;
12062306a36Sopenharmony_ci	struct MFT_REF ref;     // 0x10: File Reference of file containing attribute
12162306a36Sopenharmony_ci	__le64 open_record_lsn; // 0x18:
12262306a36Sopenharmony_ci	void *ptr;              // 0x20:
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* 32 bit version of 'struct OPEN_ATTR_ENRTY' */
12662306a36Sopenharmony_cistruct OPEN_ATTR_ENRTY_32 {
12762306a36Sopenharmony_ci	__le32 next;            // 0x00: RESTART_ENTRY_ALLOCATED if allocated
12862306a36Sopenharmony_ci	__le32 ptr;             // 0x04:
12962306a36Sopenharmony_ci	struct MFT_REF ref;     // 0x08:
13062306a36Sopenharmony_ci	__le64 open_record_lsn; // 0x10:
13162306a36Sopenharmony_ci	u8 is_dirty_pages;      // 0x18:
13262306a36Sopenharmony_ci	u8 is_attr_name;        // 0x19:
13362306a36Sopenharmony_ci	u8 res1[2];
13462306a36Sopenharmony_ci	enum ATTR_TYPE type;    // 0x1C:
13562306a36Sopenharmony_ci	u8 name_len;            // 0x20: In wchar
13662306a36Sopenharmony_ci	u8 res2[3];
13762306a36Sopenharmony_ci	__le32 AttributeName;   // 0x24:
13862306a36Sopenharmony_ci	__le32 bytes_per_index; // 0x28:
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define SIZEOF_OPENATTRIBUTEENTRY0 0x2c
14262306a36Sopenharmony_ci// static_assert( 0x2C == sizeof(struct OPEN_ATTR_ENRTY_32) );
14362306a36Sopenharmony_cistatic_assert(sizeof(struct OPEN_ATTR_ENRTY) < SIZEOF_OPENATTRIBUTEENTRY0);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * One entry exists in the Dirty Pages Table for each page which is dirty at
14762306a36Sopenharmony_ci * the time the Restart Area is written.
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistruct DIR_PAGE_ENTRY {
15062306a36Sopenharmony_ci	__le32 next;         // 0x00: RESTART_ENTRY_ALLOCATED if allocated
15162306a36Sopenharmony_ci	__le32 target_attr;  // 0x04: Index into the Open attribute Table
15262306a36Sopenharmony_ci	__le32 transfer_len; // 0x08:
15362306a36Sopenharmony_ci	__le32 lcns_follow;  // 0x0C:
15462306a36Sopenharmony_ci	__le64 vcn;          // 0x10: Vcn of dirty page
15562306a36Sopenharmony_ci	__le64 oldest_lsn;   // 0x18:
15662306a36Sopenharmony_ci	__le64 page_lcns[];  // 0x20:
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic_assert(sizeof(struct DIR_PAGE_ENTRY) == 0x20);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/* 32 bit version of 'struct DIR_PAGE_ENTRY' */
16262306a36Sopenharmony_cistruct DIR_PAGE_ENTRY_32 {
16362306a36Sopenharmony_ci	__le32 next;		// 0x00: RESTART_ENTRY_ALLOCATED if allocated
16462306a36Sopenharmony_ci	__le32 target_attr;	// 0x04: Index into the Open attribute Table
16562306a36Sopenharmony_ci	__le32 transfer_len;	// 0x08:
16662306a36Sopenharmony_ci	__le32 lcns_follow;	// 0x0C:
16762306a36Sopenharmony_ci	__le32 reserved;	// 0x10:
16862306a36Sopenharmony_ci	__le32 vcn_low;		// 0x14: Vcn of dirty page
16962306a36Sopenharmony_ci	__le32 vcn_hi;		// 0x18: Vcn of dirty page
17062306a36Sopenharmony_ci	__le32 oldest_lsn_low;	// 0x1C:
17162306a36Sopenharmony_ci	__le32 oldest_lsn_hi;	// 0x1C:
17262306a36Sopenharmony_ci	__le32 page_lcns_low;	// 0x24:
17362306a36Sopenharmony_ci	__le32 page_lcns_hi;	// 0x24:
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic_assert(offsetof(struct DIR_PAGE_ENTRY_32, vcn_low) == 0x14);
17762306a36Sopenharmony_cistatic_assert(sizeof(struct DIR_PAGE_ENTRY_32) == 0x2c);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cienum transact_state {
18062306a36Sopenharmony_ci	TransactionUninitialized = 0,
18162306a36Sopenharmony_ci	TransactionActive,
18262306a36Sopenharmony_ci	TransactionPrepared,
18362306a36Sopenharmony_ci	TransactionCommitted
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistruct TRANSACTION_ENTRY {
18762306a36Sopenharmony_ci	__le32 next;          // 0x00: RESTART_ENTRY_ALLOCATED if allocated
18862306a36Sopenharmony_ci	u8 transact_state;    // 0x04:
18962306a36Sopenharmony_ci	u8 reserved[3];       // 0x05:
19062306a36Sopenharmony_ci	__le64 first_lsn;     // 0x08:
19162306a36Sopenharmony_ci	__le64 prev_lsn;      // 0x10:
19262306a36Sopenharmony_ci	__le64 undo_next_lsn; // 0x18:
19362306a36Sopenharmony_ci	__le32 undo_records;  // 0x20: Number of undo log records pending abort
19462306a36Sopenharmony_ci	__le32 undo_len;      // 0x24: Total undo size
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic_assert(sizeof(struct TRANSACTION_ENTRY) == 0x28);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistruct NTFS_RESTART {
20062306a36Sopenharmony_ci	__le32 major_ver;             // 0x00:
20162306a36Sopenharmony_ci	__le32 minor_ver;             // 0x04:
20262306a36Sopenharmony_ci	__le64 check_point_start;     // 0x08:
20362306a36Sopenharmony_ci	__le64 open_attr_table_lsn;   // 0x10:
20462306a36Sopenharmony_ci	__le64 attr_names_lsn;        // 0x18:
20562306a36Sopenharmony_ci	__le64 dirty_pages_table_lsn; // 0x20:
20662306a36Sopenharmony_ci	__le64 transact_table_lsn;    // 0x28:
20762306a36Sopenharmony_ci	__le32 open_attr_len;         // 0x30: In bytes
20862306a36Sopenharmony_ci	__le32 attr_names_len;        // 0x34: In bytes
20962306a36Sopenharmony_ci	__le32 dirty_pages_len;       // 0x38: In bytes
21062306a36Sopenharmony_ci	__le32 transact_table_len;    // 0x3C: In bytes
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic_assert(sizeof(struct NTFS_RESTART) == 0x40);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistruct NEW_ATTRIBUTE_SIZES {
21662306a36Sopenharmony_ci	__le64 alloc_size;
21762306a36Sopenharmony_ci	__le64 valid_size;
21862306a36Sopenharmony_ci	__le64 data_size;
21962306a36Sopenharmony_ci	__le64 total_size;
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistruct BITMAP_RANGE {
22362306a36Sopenharmony_ci	__le32 bitmap_off;
22462306a36Sopenharmony_ci	__le32 bits;
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistruct LCN_RANGE {
22862306a36Sopenharmony_ci	__le64 lcn;
22962306a36Sopenharmony_ci	__le64 len;
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/* The following type defines the different log record types. */
23362306a36Sopenharmony_ci#define LfsClientRecord  cpu_to_le32(1)
23462306a36Sopenharmony_ci#define LfsClientRestart cpu_to_le32(2)
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/* This is used to uniquely identify a client for a particular log file. */
23762306a36Sopenharmony_cistruct CLIENT_ID {
23862306a36Sopenharmony_ci	__le16 seq_num;
23962306a36Sopenharmony_ci	__le16 client_idx;
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/* This is the header that begins every Log Record in the log file. */
24362306a36Sopenharmony_cistruct LFS_RECORD_HDR {
24462306a36Sopenharmony_ci	__le64 this_lsn;		// 0x00:
24562306a36Sopenharmony_ci	__le64 client_prev_lsn;		// 0x08:
24662306a36Sopenharmony_ci	__le64 client_undo_next_lsn;	// 0x10:
24762306a36Sopenharmony_ci	__le32 client_data_len;		// 0x18:
24862306a36Sopenharmony_ci	struct CLIENT_ID client;	// 0x1C: Owner of this log record.
24962306a36Sopenharmony_ci	__le32 record_type;		// 0x20: LfsClientRecord or LfsClientRestart.
25062306a36Sopenharmony_ci	__le32 transact_id;		// 0x24:
25162306a36Sopenharmony_ci	__le16 flags;			// 0x28: LOG_RECORD_MULTI_PAGE
25262306a36Sopenharmony_ci	u8 align[6];			// 0x2A:
25362306a36Sopenharmony_ci};
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci#define LOG_RECORD_MULTI_PAGE cpu_to_le16(1)
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic_assert(sizeof(struct LFS_RECORD_HDR) == 0x30);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistruct LFS_RECORD {
26062306a36Sopenharmony_ci	__le16 next_record_off;	// 0x00: Offset of the free space in the page,
26162306a36Sopenharmony_ci	u8 align[6];		// 0x02:
26262306a36Sopenharmony_ci	__le64 last_end_lsn;	// 0x08: lsn for the last log record which ends on the page,
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic_assert(sizeof(struct LFS_RECORD) == 0x10);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistruct RECORD_PAGE_HDR {
26862306a36Sopenharmony_ci	struct NTFS_RECORD_HEADER rhdr;	// 'RCRD'
26962306a36Sopenharmony_ci	__le32 rflags;			// 0x10: See LOG_PAGE_LOG_RECORD_END
27062306a36Sopenharmony_ci	__le16 page_count;		// 0x14:
27162306a36Sopenharmony_ci	__le16 page_pos;		// 0x16:
27262306a36Sopenharmony_ci	struct LFS_RECORD record_hdr;	// 0x18:
27362306a36Sopenharmony_ci	__le16 fixups[10];		// 0x28:
27462306a36Sopenharmony_ci	__le32 file_off;		// 0x3c: Used when major version >= 2
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci// clang-format on
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci// Page contains the end of a log record.
28062306a36Sopenharmony_ci#define LOG_PAGE_LOG_RECORD_END cpu_to_le32(0x00000001)
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic inline bool is_log_record_end(const struct RECORD_PAGE_HDR *hdr)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	return hdr->rflags & LOG_PAGE_LOG_RECORD_END;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic_assert(offsetof(struct RECORD_PAGE_HDR, file_off) == 0x3c);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/*
29062306a36Sopenharmony_ci * END of NTFS LOG structures
29162306a36Sopenharmony_ci */
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/* Define some tuning parameters to keep the restart tables a reasonable size. */
29462306a36Sopenharmony_ci#define INITIAL_NUMBER_TRANSACTIONS 5
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cienum NTFS_LOG_OPERATION {
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	Noop = 0x00,
29962306a36Sopenharmony_ci	CompensationLogRecord = 0x01,
30062306a36Sopenharmony_ci	InitializeFileRecordSegment = 0x02,
30162306a36Sopenharmony_ci	DeallocateFileRecordSegment = 0x03,
30262306a36Sopenharmony_ci	WriteEndOfFileRecordSegment = 0x04,
30362306a36Sopenharmony_ci	CreateAttribute = 0x05,
30462306a36Sopenharmony_ci	DeleteAttribute = 0x06,
30562306a36Sopenharmony_ci	UpdateResidentValue = 0x07,
30662306a36Sopenharmony_ci	UpdateNonresidentValue = 0x08,
30762306a36Sopenharmony_ci	UpdateMappingPairs = 0x09,
30862306a36Sopenharmony_ci	DeleteDirtyClusters = 0x0A,
30962306a36Sopenharmony_ci	SetNewAttributeSizes = 0x0B,
31062306a36Sopenharmony_ci	AddIndexEntryRoot = 0x0C,
31162306a36Sopenharmony_ci	DeleteIndexEntryRoot = 0x0D,
31262306a36Sopenharmony_ci	AddIndexEntryAllocation = 0x0E,
31362306a36Sopenharmony_ci	DeleteIndexEntryAllocation = 0x0F,
31462306a36Sopenharmony_ci	WriteEndOfIndexBuffer = 0x10,
31562306a36Sopenharmony_ci	SetIndexEntryVcnRoot = 0x11,
31662306a36Sopenharmony_ci	SetIndexEntryVcnAllocation = 0x12,
31762306a36Sopenharmony_ci	UpdateFileNameRoot = 0x13,
31862306a36Sopenharmony_ci	UpdateFileNameAllocation = 0x14,
31962306a36Sopenharmony_ci	SetBitsInNonresidentBitMap = 0x15,
32062306a36Sopenharmony_ci	ClearBitsInNonresidentBitMap = 0x16,
32162306a36Sopenharmony_ci	HotFix = 0x17,
32262306a36Sopenharmony_ci	EndTopLevelAction = 0x18,
32362306a36Sopenharmony_ci	PrepareTransaction = 0x19,
32462306a36Sopenharmony_ci	CommitTransaction = 0x1A,
32562306a36Sopenharmony_ci	ForgetTransaction = 0x1B,
32662306a36Sopenharmony_ci	OpenNonresidentAttribute = 0x1C,
32762306a36Sopenharmony_ci	OpenAttributeTableDump = 0x1D,
32862306a36Sopenharmony_ci	AttributeNamesDump = 0x1E,
32962306a36Sopenharmony_ci	DirtyPageTableDump = 0x1F,
33062306a36Sopenharmony_ci	TransactionTableDump = 0x20,
33162306a36Sopenharmony_ci	UpdateRecordDataRoot = 0x21,
33262306a36Sopenharmony_ci	UpdateRecordDataAllocation = 0x22,
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	UpdateRelativeDataInIndex =
33562306a36Sopenharmony_ci		0x23, // NtOfsRestartUpdateRelativeDataInIndex
33662306a36Sopenharmony_ci	UpdateRelativeDataInIndex2 = 0x24,
33762306a36Sopenharmony_ci	ZeroEndOfFileRecord = 0x25,
33862306a36Sopenharmony_ci};
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/*
34162306a36Sopenharmony_ci * Array for log records which require a target attribute.
34262306a36Sopenharmony_ci * A true indicates that the corresponding restart operation
34362306a36Sopenharmony_ci * requires a target attribute.
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_cistatic const u8 AttributeRequired[] = {
34662306a36Sopenharmony_ci	0xFC, 0xFB, 0xFF, 0x10, 0x06,
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic inline bool is_target_required(u16 op)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	bool ret = op <= UpdateRecordDataAllocation &&
35262306a36Sopenharmony_ci		   (AttributeRequired[op >> 3] >> (op & 7) & 1);
35362306a36Sopenharmony_ci	return ret;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic inline bool can_skip_action(enum NTFS_LOG_OPERATION op)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	switch (op) {
35962306a36Sopenharmony_ci	case Noop:
36062306a36Sopenharmony_ci	case DeleteDirtyClusters:
36162306a36Sopenharmony_ci	case HotFix:
36262306a36Sopenharmony_ci	case EndTopLevelAction:
36362306a36Sopenharmony_ci	case PrepareTransaction:
36462306a36Sopenharmony_ci	case CommitTransaction:
36562306a36Sopenharmony_ci	case ForgetTransaction:
36662306a36Sopenharmony_ci	case CompensationLogRecord:
36762306a36Sopenharmony_ci	case OpenNonresidentAttribute:
36862306a36Sopenharmony_ci	case OpenAttributeTableDump:
36962306a36Sopenharmony_ci	case AttributeNamesDump:
37062306a36Sopenharmony_ci	case DirtyPageTableDump:
37162306a36Sopenharmony_ci	case TransactionTableDump:
37262306a36Sopenharmony_ci		return true;
37362306a36Sopenharmony_ci	default:
37462306a36Sopenharmony_ci		return false;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cienum { lcb_ctx_undo_next, lcb_ctx_prev, lcb_ctx_next };
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/* Bytes per restart table. */
38162306a36Sopenharmony_cistatic inline u32 bytes_per_rt(const struct RESTART_TABLE *rt)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	return le16_to_cpu(rt->used) * le16_to_cpu(rt->size) +
38462306a36Sopenharmony_ci	       sizeof(struct RESTART_TABLE);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/* Log record length. */
38862306a36Sopenharmony_cistatic inline u32 lrh_length(const struct LOG_REC_HDR *lr)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	u16 t16 = le16_to_cpu(lr->lcns_follow);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return struct_size(lr, page_lcns, max_t(u16, 1, t16));
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistruct lcb {
39662306a36Sopenharmony_ci	struct LFS_RECORD_HDR *lrh; // Log record header of the current lsn.
39762306a36Sopenharmony_ci	struct LOG_REC_HDR *log_rec;
39862306a36Sopenharmony_ci	u32 ctx_mode; // lcb_ctx_undo_next/lcb_ctx_prev/lcb_ctx_next
39962306a36Sopenharmony_ci	struct CLIENT_ID client;
40062306a36Sopenharmony_ci	bool alloc; // If true the we should deallocate 'log_rec'.
40162306a36Sopenharmony_ci};
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic void lcb_put(struct lcb *lcb)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	if (lcb->alloc)
40662306a36Sopenharmony_ci		kfree(lcb->log_rec);
40762306a36Sopenharmony_ci	kfree(lcb->lrh);
40862306a36Sopenharmony_ci	kfree(lcb);
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/* Find the oldest lsn from active clients. */
41262306a36Sopenharmony_cistatic inline void oldest_client_lsn(const struct CLIENT_REC *ca,
41362306a36Sopenharmony_ci				     __le16 next_client, u64 *oldest_lsn)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	while (next_client != LFS_NO_CLIENT_LE) {
41662306a36Sopenharmony_ci		const struct CLIENT_REC *cr = ca + le16_to_cpu(next_client);
41762306a36Sopenharmony_ci		u64 lsn = le64_to_cpu(cr->oldest_lsn);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		/* Ignore this block if it's oldest lsn is 0. */
42062306a36Sopenharmony_ci		if (lsn && lsn < *oldest_lsn)
42162306a36Sopenharmony_ci			*oldest_lsn = lsn;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		next_client = cr->next_client;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic inline bool is_rst_page_hdr_valid(u32 file_off,
42862306a36Sopenharmony_ci					 const struct RESTART_HDR *rhdr)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	u32 sys_page = le32_to_cpu(rhdr->sys_page_size);
43162306a36Sopenharmony_ci	u32 page_size = le32_to_cpu(rhdr->page_size);
43262306a36Sopenharmony_ci	u32 end_usa;
43362306a36Sopenharmony_ci	u16 ro;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (sys_page < SECTOR_SIZE || page_size < SECTOR_SIZE ||
43662306a36Sopenharmony_ci	    sys_page & (sys_page - 1) || page_size & (page_size - 1)) {
43762306a36Sopenharmony_ci		return false;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* Check that if the file offset isn't 0, it is the system page size. */
44162306a36Sopenharmony_ci	if (file_off && file_off != sys_page)
44262306a36Sopenharmony_ci		return false;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* Check support version 1.1+. */
44562306a36Sopenharmony_ci	if (le16_to_cpu(rhdr->major_ver) <= 1 && !rhdr->minor_ver)
44662306a36Sopenharmony_ci		return false;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (le16_to_cpu(rhdr->major_ver) > 2)
44962306a36Sopenharmony_ci		return false;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	ro = le16_to_cpu(rhdr->ra_off);
45262306a36Sopenharmony_ci	if (!IS_ALIGNED(ro, 8) || ro > sys_page)
45362306a36Sopenharmony_ci		return false;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	end_usa = ((sys_page >> SECTOR_SHIFT) + 1) * sizeof(short);
45662306a36Sopenharmony_ci	end_usa += le16_to_cpu(rhdr->rhdr.fix_off);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (ro < end_usa)
45962306a36Sopenharmony_ci		return false;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return true;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	const struct RESTART_AREA *ra;
46762306a36Sopenharmony_ci	u16 cl, fl, ul;
46862306a36Sopenharmony_ci	u32 off, l_size, seq_bits;
46962306a36Sopenharmony_ci	u16 ro = le16_to_cpu(rhdr->ra_off);
47062306a36Sopenharmony_ci	u32 sys_page = le32_to_cpu(rhdr->sys_page_size);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (ro + offsetof(struct RESTART_AREA, l_size) >
47362306a36Sopenharmony_ci	    SECTOR_SIZE - sizeof(short))
47462306a36Sopenharmony_ci		return false;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ra = Add2Ptr(rhdr, ro);
47762306a36Sopenharmony_ci	cl = le16_to_cpu(ra->log_clients);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (cl > 1)
48062306a36Sopenharmony_ci		return false;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	off = le16_to_cpu(ra->client_off);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (!IS_ALIGNED(off, 8) || ro + off > SECTOR_SIZE - sizeof(short))
48562306a36Sopenharmony_ci		return false;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	off += cl * sizeof(struct CLIENT_REC);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (off > sys_page)
49062306a36Sopenharmony_ci		return false;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/*
49362306a36Sopenharmony_ci	 * Check the restart length field and whether the entire
49462306a36Sopenharmony_ci	 * restart area is contained that length.
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci	if (le16_to_cpu(rhdr->ra_off) + le16_to_cpu(ra->ra_len) > sys_page ||
49762306a36Sopenharmony_ci	    off > le16_to_cpu(ra->ra_len)) {
49862306a36Sopenharmony_ci		return false;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/*
50262306a36Sopenharmony_ci	 * As a final check make sure that the use list and the free list
50362306a36Sopenharmony_ci	 * are either empty or point to a valid client.
50462306a36Sopenharmony_ci	 */
50562306a36Sopenharmony_ci	fl = le16_to_cpu(ra->client_idx[0]);
50662306a36Sopenharmony_ci	ul = le16_to_cpu(ra->client_idx[1]);
50762306a36Sopenharmony_ci	if ((fl != LFS_NO_CLIENT && fl >= cl) ||
50862306a36Sopenharmony_ci	    (ul != LFS_NO_CLIENT && ul >= cl))
50962306a36Sopenharmony_ci		return false;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* Make sure the sequence number bits match the log file size. */
51262306a36Sopenharmony_ci	l_size = le64_to_cpu(ra->l_size);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	seq_bits = sizeof(u64) * 8 + 3;
51562306a36Sopenharmony_ci	while (l_size) {
51662306a36Sopenharmony_ci		l_size >>= 1;
51762306a36Sopenharmony_ci		seq_bits -= 1;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (seq_bits != ra->seq_num_bits)
52162306a36Sopenharmony_ci		return false;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* The log page data offset and record header length must be quad-aligned. */
52462306a36Sopenharmony_ci	if (!IS_ALIGNED(le16_to_cpu(ra->data_off), 8) ||
52562306a36Sopenharmony_ci	    !IS_ALIGNED(le16_to_cpu(ra->rec_hdr_len), 8))
52662306a36Sopenharmony_ci		return false;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return true;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic inline bool is_client_area_valid(const struct RESTART_HDR *rhdr,
53262306a36Sopenharmony_ci					bool usa_error)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	u16 ro = le16_to_cpu(rhdr->ra_off);
53562306a36Sopenharmony_ci	const struct RESTART_AREA *ra = Add2Ptr(rhdr, ro);
53662306a36Sopenharmony_ci	u16 ra_len = le16_to_cpu(ra->ra_len);
53762306a36Sopenharmony_ci	const struct CLIENT_REC *ca;
53862306a36Sopenharmony_ci	u32 i;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (usa_error && ra_len + ro > SECTOR_SIZE - sizeof(short))
54162306a36Sopenharmony_ci		return false;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* Find the start of the client array. */
54462306a36Sopenharmony_ci	ca = Add2Ptr(ra, le16_to_cpu(ra->client_off));
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/*
54762306a36Sopenharmony_ci	 * Start with the free list.
54862306a36Sopenharmony_ci	 * Check that all the clients are valid and that there isn't a cycle.
54962306a36Sopenharmony_ci	 * Do the in-use list on the second pass.
55062306a36Sopenharmony_ci	 */
55162306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
55262306a36Sopenharmony_ci		u16 client_idx = le16_to_cpu(ra->client_idx[i]);
55362306a36Sopenharmony_ci		bool first_client = true;
55462306a36Sopenharmony_ci		u16 clients = le16_to_cpu(ra->log_clients);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		while (client_idx != LFS_NO_CLIENT) {
55762306a36Sopenharmony_ci			const struct CLIENT_REC *cr;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci			if (!clients ||
56062306a36Sopenharmony_ci			    client_idx >= le16_to_cpu(ra->log_clients))
56162306a36Sopenharmony_ci				return false;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci			clients -= 1;
56462306a36Sopenharmony_ci			cr = ca + client_idx;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci			client_idx = le16_to_cpu(cr->next_client);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			if (first_client) {
56962306a36Sopenharmony_ci				first_client = false;
57062306a36Sopenharmony_ci				if (cr->prev_client != LFS_NO_CLIENT_LE)
57162306a36Sopenharmony_ci					return false;
57262306a36Sopenharmony_ci			}
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return true;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/*
58062306a36Sopenharmony_ci * remove_client
58162306a36Sopenharmony_ci *
58262306a36Sopenharmony_ci * Remove a client record from a client record list an restart area.
58362306a36Sopenharmony_ci */
58462306a36Sopenharmony_cistatic inline void remove_client(struct CLIENT_REC *ca,
58562306a36Sopenharmony_ci				 const struct CLIENT_REC *cr, __le16 *head)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	if (cr->prev_client == LFS_NO_CLIENT_LE)
58862306a36Sopenharmony_ci		*head = cr->next_client;
58962306a36Sopenharmony_ci	else
59062306a36Sopenharmony_ci		ca[le16_to_cpu(cr->prev_client)].next_client = cr->next_client;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (cr->next_client != LFS_NO_CLIENT_LE)
59362306a36Sopenharmony_ci		ca[le16_to_cpu(cr->next_client)].prev_client = cr->prev_client;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/*
59762306a36Sopenharmony_ci * add_client - Add a client record to the start of a list.
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_cistatic inline void add_client(struct CLIENT_REC *ca, u16 index, __le16 *head)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct CLIENT_REC *cr = ca + index;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	cr->prev_client = LFS_NO_CLIENT_LE;
60462306a36Sopenharmony_ci	cr->next_client = *head;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (*head != LFS_NO_CLIENT_LE)
60762306a36Sopenharmony_ci		ca[le16_to_cpu(*head)].prev_client = cpu_to_le16(index);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	*head = cpu_to_le16(index);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic inline void *enum_rstbl(struct RESTART_TABLE *t, void *c)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	__le32 *e;
61562306a36Sopenharmony_ci	u32 bprt;
61662306a36Sopenharmony_ci	u16 rsize = t ? le16_to_cpu(t->size) : 0;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (!c) {
61962306a36Sopenharmony_ci		if (!t || !t->total)
62062306a36Sopenharmony_ci			return NULL;
62162306a36Sopenharmony_ci		e = Add2Ptr(t, sizeof(struct RESTART_TABLE));
62262306a36Sopenharmony_ci	} else {
62362306a36Sopenharmony_ci		e = Add2Ptr(c, rsize);
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* Loop until we hit the first one allocated, or the end of the list. */
62762306a36Sopenharmony_ci	for (bprt = bytes_per_rt(t); PtrOffset(t, e) < bprt;
62862306a36Sopenharmony_ci	     e = Add2Ptr(e, rsize)) {
62962306a36Sopenharmony_ci		if (*e == RESTART_ENTRY_ALLOCATED_LE)
63062306a36Sopenharmony_ci			return e;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	return NULL;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * find_dp - Search for a @vcn in Dirty Page Table.
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_cistatic inline struct DIR_PAGE_ENTRY *find_dp(struct RESTART_TABLE *dptbl,
63962306a36Sopenharmony_ci					     u32 target_attr, u64 vcn)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	__le32 ta = cpu_to_le32(target_attr);
64262306a36Sopenharmony_ci	struct DIR_PAGE_ENTRY *dp = NULL;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	while ((dp = enum_rstbl(dptbl, dp))) {
64562306a36Sopenharmony_ci		u64 dp_vcn = le64_to_cpu(dp->vcn);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		if (dp->target_attr == ta && vcn >= dp_vcn &&
64862306a36Sopenharmony_ci		    vcn < dp_vcn + le32_to_cpu(dp->lcns_follow)) {
64962306a36Sopenharmony_ci			return dp;
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci	return NULL;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic inline u32 norm_file_page(u32 page_size, u32 *l_size, bool use_default)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	if (use_default)
65862306a36Sopenharmony_ci		page_size = DefaultLogPageSize;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/* Round the file size down to a system page boundary. */
66162306a36Sopenharmony_ci	*l_size &= ~(page_size - 1);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* File should contain at least 2 restart pages and MinLogRecordPages pages. */
66462306a36Sopenharmony_ci	if (*l_size < (MinLogRecordPages + 2) * page_size)
66562306a36Sopenharmony_ci		return 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	return page_size;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic bool check_log_rec(const struct LOG_REC_HDR *lr, u32 bytes, u32 tr,
67162306a36Sopenharmony_ci			  u32 bytes_per_attr_entry)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	u16 t16;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (bytes < sizeof(struct LOG_REC_HDR))
67662306a36Sopenharmony_ci		return false;
67762306a36Sopenharmony_ci	if (!tr)
67862306a36Sopenharmony_ci		return false;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if ((tr - sizeof(struct RESTART_TABLE)) %
68162306a36Sopenharmony_ci	    sizeof(struct TRANSACTION_ENTRY))
68262306a36Sopenharmony_ci		return false;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (le16_to_cpu(lr->redo_off) & 7)
68562306a36Sopenharmony_ci		return false;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (le16_to_cpu(lr->undo_off) & 7)
68862306a36Sopenharmony_ci		return false;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (lr->target_attr)
69162306a36Sopenharmony_ci		goto check_lcns;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (is_target_required(le16_to_cpu(lr->redo_op)))
69462306a36Sopenharmony_ci		return false;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (is_target_required(le16_to_cpu(lr->undo_op)))
69762306a36Sopenharmony_ci		return false;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cicheck_lcns:
70062306a36Sopenharmony_ci	if (!lr->lcns_follow)
70162306a36Sopenharmony_ci		goto check_length;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	t16 = le16_to_cpu(lr->target_attr);
70462306a36Sopenharmony_ci	if ((t16 - sizeof(struct RESTART_TABLE)) % bytes_per_attr_entry)
70562306a36Sopenharmony_ci		return false;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cicheck_length:
70862306a36Sopenharmony_ci	if (bytes < lrh_length(lr))
70962306a36Sopenharmony_ci		return false;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return true;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	u32 ts;
71762306a36Sopenharmony_ci	u32 i, off;
71862306a36Sopenharmony_ci	u16 rsize = le16_to_cpu(rt->size);
71962306a36Sopenharmony_ci	u16 ne = le16_to_cpu(rt->used);
72062306a36Sopenharmony_ci	u32 ff = le32_to_cpu(rt->first_free);
72162306a36Sopenharmony_ci	u32 lf = le32_to_cpu(rt->last_free);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	ts = rsize * ne + sizeof(struct RESTART_TABLE);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (!rsize || rsize > bytes ||
72662306a36Sopenharmony_ci	    rsize + sizeof(struct RESTART_TABLE) > bytes || bytes < ts ||
72762306a36Sopenharmony_ci	    le16_to_cpu(rt->total) > ne || ff > ts || lf > ts ||
72862306a36Sopenharmony_ci	    (ff && ff < sizeof(struct RESTART_TABLE)) ||
72962306a36Sopenharmony_ci	    (lf && lf < sizeof(struct RESTART_TABLE))) {
73062306a36Sopenharmony_ci		return false;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/*
73462306a36Sopenharmony_ci	 * Verify each entry is either allocated or points
73562306a36Sopenharmony_ci	 * to a valid offset the table.
73662306a36Sopenharmony_ci	 */
73762306a36Sopenharmony_ci	for (i = 0; i < ne; i++) {
73862306a36Sopenharmony_ci		off = le32_to_cpu(*(__le32 *)Add2Ptr(
73962306a36Sopenharmony_ci			rt, i * rsize + sizeof(struct RESTART_TABLE)));
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		if (off != RESTART_ENTRY_ALLOCATED && off &&
74262306a36Sopenharmony_ci		    (off < sizeof(struct RESTART_TABLE) ||
74362306a36Sopenharmony_ci		     ((off - sizeof(struct RESTART_TABLE)) % rsize))) {
74462306a36Sopenharmony_ci			return false;
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	/*
74962306a36Sopenharmony_ci	 * Walk through the list headed by the first entry to make
75062306a36Sopenharmony_ci	 * sure none of the entries are currently being used.
75162306a36Sopenharmony_ci	 */
75262306a36Sopenharmony_ci	for (off = ff; off;) {
75362306a36Sopenharmony_ci		if (off == RESTART_ENTRY_ALLOCATED)
75462306a36Sopenharmony_ci			return false;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off));
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return true;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci/*
76362306a36Sopenharmony_ci * free_rsttbl_idx - Free a previously allocated index a Restart Table.
76462306a36Sopenharmony_ci */
76562306a36Sopenharmony_cistatic inline void free_rsttbl_idx(struct RESTART_TABLE *rt, u32 off)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	__le32 *e;
76862306a36Sopenharmony_ci	u32 lf = le32_to_cpu(rt->last_free);
76962306a36Sopenharmony_ci	__le32 off_le = cpu_to_le32(off);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	e = Add2Ptr(rt, off);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (off < le32_to_cpu(rt->free_goal)) {
77462306a36Sopenharmony_ci		*e = rt->first_free;
77562306a36Sopenharmony_ci		rt->first_free = off_le;
77662306a36Sopenharmony_ci		if (!lf)
77762306a36Sopenharmony_ci			rt->last_free = off_le;
77862306a36Sopenharmony_ci	} else {
77962306a36Sopenharmony_ci		if (lf)
78062306a36Sopenharmony_ci			*(__le32 *)Add2Ptr(rt, lf) = off_le;
78162306a36Sopenharmony_ci		else
78262306a36Sopenharmony_ci			rt->first_free = off_le;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		rt->last_free = off_le;
78562306a36Sopenharmony_ci		*e = 0;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	le16_sub_cpu(&rt->total, 1);
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic inline struct RESTART_TABLE *init_rsttbl(u16 esize, u16 used)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	__le32 *e, *last_free;
79462306a36Sopenharmony_ci	u32 off;
79562306a36Sopenharmony_ci	u32 bytes = esize * used + sizeof(struct RESTART_TABLE);
79662306a36Sopenharmony_ci	u32 lf = sizeof(struct RESTART_TABLE) + (used - 1) * esize;
79762306a36Sopenharmony_ci	struct RESTART_TABLE *t = kzalloc(bytes, GFP_NOFS);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (!t)
80062306a36Sopenharmony_ci		return NULL;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	t->size = cpu_to_le16(esize);
80362306a36Sopenharmony_ci	t->used = cpu_to_le16(used);
80462306a36Sopenharmony_ci	t->free_goal = cpu_to_le32(~0u);
80562306a36Sopenharmony_ci	t->first_free = cpu_to_le32(sizeof(struct RESTART_TABLE));
80662306a36Sopenharmony_ci	t->last_free = cpu_to_le32(lf);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	e = (__le32 *)(t + 1);
80962306a36Sopenharmony_ci	last_free = Add2Ptr(t, lf);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	for (off = sizeof(struct RESTART_TABLE) + esize; e < last_free;
81262306a36Sopenharmony_ci	     e = Add2Ptr(e, esize), off += esize) {
81362306a36Sopenharmony_ci		*e = cpu_to_le32(off);
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci	return t;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_cistatic inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl,
81962306a36Sopenharmony_ci						  u32 add, u32 free_goal)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	u16 esize = le16_to_cpu(tbl->size);
82262306a36Sopenharmony_ci	__le32 osize = cpu_to_le32(bytes_per_rt(tbl));
82362306a36Sopenharmony_ci	u32 used = le16_to_cpu(tbl->used);
82462306a36Sopenharmony_ci	struct RESTART_TABLE *rt;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	rt = init_rsttbl(esize, used + add);
82762306a36Sopenharmony_ci	if (!rt)
82862306a36Sopenharmony_ci		return NULL;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	memcpy(rt + 1, tbl + 1, esize * used);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	rt->free_goal = free_goal == ~0u ?
83362306a36Sopenharmony_ci				cpu_to_le32(~0u) :
83462306a36Sopenharmony_ci				cpu_to_le32(sizeof(struct RESTART_TABLE) +
83562306a36Sopenharmony_ci					    free_goal * esize);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (tbl->first_free) {
83862306a36Sopenharmony_ci		rt->first_free = tbl->first_free;
83962306a36Sopenharmony_ci		*(__le32 *)Add2Ptr(rt, le32_to_cpu(tbl->last_free)) = osize;
84062306a36Sopenharmony_ci	} else {
84162306a36Sopenharmony_ci		rt->first_free = osize;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	rt->total = tbl->total;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	kfree(tbl);
84762306a36Sopenharmony_ci	return rt;
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/*
85162306a36Sopenharmony_ci * alloc_rsttbl_idx
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * Allocate an index from within a previously initialized Restart Table.
85462306a36Sopenharmony_ci */
85562306a36Sopenharmony_cistatic inline void *alloc_rsttbl_idx(struct RESTART_TABLE **tbl)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	u32 off;
85862306a36Sopenharmony_ci	__le32 *e;
85962306a36Sopenharmony_ci	struct RESTART_TABLE *t = *tbl;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (!t->first_free) {
86262306a36Sopenharmony_ci		*tbl = t = extend_rsttbl(t, 16, ~0u);
86362306a36Sopenharmony_ci		if (!t)
86462306a36Sopenharmony_ci			return NULL;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	off = le32_to_cpu(t->first_free);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/* Dequeue this entry and zero it. */
87062306a36Sopenharmony_ci	e = Add2Ptr(t, off);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	t->first_free = *e;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	memset(e, 0, le16_to_cpu(t->size));
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	*e = RESTART_ENTRY_ALLOCATED_LE;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* If list is going empty, then we fix the last_free as well. */
87962306a36Sopenharmony_ci	if (!t->first_free)
88062306a36Sopenharmony_ci		t->last_free = 0;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	le16_add_cpu(&t->total, 1);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return Add2Ptr(t, off);
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci/*
88862306a36Sopenharmony_ci * alloc_rsttbl_from_idx
88962306a36Sopenharmony_ci *
89062306a36Sopenharmony_ci * Allocate a specific index from within a previously initialized Restart Table.
89162306a36Sopenharmony_ci */
89262306a36Sopenharmony_cistatic inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	u32 off;
89562306a36Sopenharmony_ci	__le32 *e;
89662306a36Sopenharmony_ci	struct RESTART_TABLE *rt = *tbl;
89762306a36Sopenharmony_ci	u32 bytes = bytes_per_rt(rt);
89862306a36Sopenharmony_ci	u16 esize = le16_to_cpu(rt->size);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* If the entry is not the table, we will have to extend the table. */
90162306a36Sopenharmony_ci	if (vbo >= bytes) {
90262306a36Sopenharmony_ci		/*
90362306a36Sopenharmony_ci		 * Extend the size by computing the number of entries between
90462306a36Sopenharmony_ci		 * the existing size and the desired index and adding 1 to that.
90562306a36Sopenharmony_ci		 */
90662306a36Sopenharmony_ci		u32 bytes2idx = vbo - bytes;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		/*
90962306a36Sopenharmony_ci		 * There should always be an integral number of entries
91062306a36Sopenharmony_ci		 * being added. Now extend the table.
91162306a36Sopenharmony_ci		 */
91262306a36Sopenharmony_ci		*tbl = rt = extend_rsttbl(rt, bytes2idx / esize + 1, bytes);
91362306a36Sopenharmony_ci		if (!rt)
91462306a36Sopenharmony_ci			return NULL;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	/* See if the entry is already allocated, and just return if it is. */
91862306a36Sopenharmony_ci	e = Add2Ptr(rt, vbo);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (*e == RESTART_ENTRY_ALLOCATED_LE)
92162306a36Sopenharmony_ci		return e;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/*
92462306a36Sopenharmony_ci	 * Walk through the table, looking for the entry we're
92562306a36Sopenharmony_ci	 * interested and the previous entry.
92662306a36Sopenharmony_ci	 */
92762306a36Sopenharmony_ci	off = le32_to_cpu(rt->first_free);
92862306a36Sopenharmony_ci	e = Add2Ptr(rt, off);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (off == vbo) {
93162306a36Sopenharmony_ci		/* this is a match */
93262306a36Sopenharmony_ci		rt->first_free = *e;
93362306a36Sopenharmony_ci		goto skip_looking;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/*
93762306a36Sopenharmony_ci	 * Need to walk through the list looking for the predecessor
93862306a36Sopenharmony_ci	 * of our entry.
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_ci	for (;;) {
94162306a36Sopenharmony_ci		/* Remember the entry just found */
94262306a36Sopenharmony_ci		u32 last_off = off;
94362306a36Sopenharmony_ci		__le32 *last_e = e;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		/* Should never run of entries. */
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci		/* Lookup up the next entry the list. */
94862306a36Sopenharmony_ci		off = le32_to_cpu(*last_e);
94962306a36Sopenharmony_ci		e = Add2Ptr(rt, off);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		/* If this is our match we are done. */
95262306a36Sopenharmony_ci		if (off == vbo) {
95362306a36Sopenharmony_ci			*last_e = *e;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci			/*
95662306a36Sopenharmony_ci			 * If this was the last entry, we update that
95762306a36Sopenharmony_ci			 * table as well.
95862306a36Sopenharmony_ci			 */
95962306a36Sopenharmony_ci			if (le32_to_cpu(rt->last_free) == off)
96062306a36Sopenharmony_ci				rt->last_free = cpu_to_le32(last_off);
96162306a36Sopenharmony_ci			break;
96262306a36Sopenharmony_ci		}
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ciskip_looking:
96662306a36Sopenharmony_ci	/* If the list is now empty, we fix the last_free as well. */
96762306a36Sopenharmony_ci	if (!rt->first_free)
96862306a36Sopenharmony_ci		rt->last_free = 0;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	/* Zero this entry. */
97162306a36Sopenharmony_ci	memset(e, 0, esize);
97262306a36Sopenharmony_ci	*e = RESTART_ENTRY_ALLOCATED_LE;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	le16_add_cpu(&rt->total, 1);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	return e;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistruct restart_info {
98062306a36Sopenharmony_ci	u64 last_lsn;
98162306a36Sopenharmony_ci	struct RESTART_HDR *r_page;
98262306a36Sopenharmony_ci	u32 vbo;
98362306a36Sopenharmony_ci	bool chkdsk_was_run;
98462306a36Sopenharmony_ci	bool valid_page;
98562306a36Sopenharmony_ci	bool initialized;
98662306a36Sopenharmony_ci	bool restart;
98762306a36Sopenharmony_ci};
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci#define RESTART_SINGLE_PAGE_IO cpu_to_le16(0x0001)
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci#define NTFSLOG_WRAPPED 0x00000001
99262306a36Sopenharmony_ci#define NTFSLOG_MULTIPLE_PAGE_IO 0x00000002
99362306a36Sopenharmony_ci#define NTFSLOG_NO_LAST_LSN 0x00000004
99462306a36Sopenharmony_ci#define NTFSLOG_REUSE_TAIL 0x00000010
99562306a36Sopenharmony_ci#define NTFSLOG_NO_OLDEST_LSN 0x00000020
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci/* Helper struct to work with NTFS $LogFile. */
99862306a36Sopenharmony_cistruct ntfs_log {
99962306a36Sopenharmony_ci	struct ntfs_inode *ni;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	u32 l_size;
100262306a36Sopenharmony_ci	u32 orig_file_size;
100362306a36Sopenharmony_ci	u32 sys_page_size;
100462306a36Sopenharmony_ci	u32 sys_page_mask;
100562306a36Sopenharmony_ci	u32 page_size;
100662306a36Sopenharmony_ci	u32 page_mask; // page_size - 1
100762306a36Sopenharmony_ci	u8 page_bits;
100862306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *one_page_buf;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	struct RESTART_TABLE *open_attr_tbl;
101162306a36Sopenharmony_ci	u32 transaction_id;
101262306a36Sopenharmony_ci	u32 clst_per_page;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	u32 first_page;
101562306a36Sopenharmony_ci	u32 next_page;
101662306a36Sopenharmony_ci	u32 ra_off;
101762306a36Sopenharmony_ci	u32 data_off;
101862306a36Sopenharmony_ci	u32 restart_size;
101962306a36Sopenharmony_ci	u32 data_size;
102062306a36Sopenharmony_ci	u16 record_header_len;
102162306a36Sopenharmony_ci	u64 seq_num;
102262306a36Sopenharmony_ci	u32 seq_num_bits;
102362306a36Sopenharmony_ci	u32 file_data_bits;
102462306a36Sopenharmony_ci	u32 seq_num_mask; /* (1 << file_data_bits) - 1 */
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	struct RESTART_AREA *ra; /* In-memory image of the next restart area. */
102762306a36Sopenharmony_ci	u32 ra_size; /* The usable size of the restart area. */
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/*
103062306a36Sopenharmony_ci	 * If true, then the in-memory restart area is to be written
103162306a36Sopenharmony_ci	 * to the first position on the disk.
103262306a36Sopenharmony_ci	 */
103362306a36Sopenharmony_ci	bool init_ra;
103462306a36Sopenharmony_ci	bool set_dirty; /* True if we need to set dirty flag. */
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	u64 oldest_lsn;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	u32 oldest_lsn_off;
103962306a36Sopenharmony_ci	u64 last_lsn;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	u32 total_avail;
104262306a36Sopenharmony_ci	u32 total_avail_pages;
104362306a36Sopenharmony_ci	u32 total_undo_commit;
104462306a36Sopenharmony_ci	u32 max_current_avail;
104562306a36Sopenharmony_ci	u32 current_avail;
104662306a36Sopenharmony_ci	u32 reserved;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	short major_ver;
104962306a36Sopenharmony_ci	short minor_ver;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	u32 l_flags; /* See NTFSLOG_XXX */
105262306a36Sopenharmony_ci	u32 current_openlog_count; /* On-disk value for open_log_count. */
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	struct CLIENT_ID client_id;
105562306a36Sopenharmony_ci	u32 client_undo_commit;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	struct restart_info rst_info, rst_info2;
105862306a36Sopenharmony_ci};
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic inline u32 lsn_to_vbo(struct ntfs_log *log, const u64 lsn)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	u32 vbo = (lsn << log->seq_num_bits) >> (log->seq_num_bits - 3);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	return vbo;
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci/* Compute the offset in the log file of the next log page. */
106862306a36Sopenharmony_cistatic inline u32 next_page_off(struct ntfs_log *log, u32 off)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	off = (off & ~log->sys_page_mask) + log->page_size;
107162306a36Sopenharmony_ci	return off >= log->l_size ? log->first_page : off;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic inline u32 lsn_to_page_off(struct ntfs_log *log, u64 lsn)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	return (((u32)lsn) << 3) & log->page_mask;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic inline u64 vbo_to_lsn(struct ntfs_log *log, u32 off, u64 Seq)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	return (off >> 3) + (Seq << log->file_data_bits);
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic inline bool is_lsn_in_file(struct ntfs_log *log, u64 lsn)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	return lsn >= log->oldest_lsn &&
108762306a36Sopenharmony_ci	       lsn <= le64_to_cpu(log->ra->current_lsn);
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic inline u32 hdr_file_off(struct ntfs_log *log,
109162306a36Sopenharmony_ci			       struct RECORD_PAGE_HDR *hdr)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	if (log->major_ver < 2)
109462306a36Sopenharmony_ci		return le64_to_cpu(hdr->rhdr.lsn);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	return le32_to_cpu(hdr->file_off);
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic inline u64 base_lsn(struct ntfs_log *log,
110062306a36Sopenharmony_ci			   const struct RECORD_PAGE_HDR *hdr, u64 lsn)
110162306a36Sopenharmony_ci{
110262306a36Sopenharmony_ci	u64 h_lsn = le64_to_cpu(hdr->rhdr.lsn);
110362306a36Sopenharmony_ci	u64 ret = (((h_lsn >> log->file_data_bits) +
110462306a36Sopenharmony_ci		    (lsn < (lsn_to_vbo(log, h_lsn) & ~log->page_mask) ? 1 : 0))
110562306a36Sopenharmony_ci		   << log->file_data_bits) +
110662306a36Sopenharmony_ci		  ((((is_log_record_end(hdr) &&
110762306a36Sopenharmony_ci		      h_lsn <= le64_to_cpu(hdr->record_hdr.last_end_lsn)) ?
110862306a36Sopenharmony_ci			     le16_to_cpu(hdr->record_hdr.next_record_off) :
110962306a36Sopenharmony_ci			     log->page_size) +
111062306a36Sopenharmony_ci		    lsn) >>
111162306a36Sopenharmony_ci		   3);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	return ret;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic inline bool verify_client_lsn(struct ntfs_log *log,
111762306a36Sopenharmony_ci				     const struct CLIENT_REC *client, u64 lsn)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	return lsn >= le64_to_cpu(client->oldest_lsn) &&
112062306a36Sopenharmony_ci	       lsn <= le64_to_cpu(log->ra->current_lsn) && lsn;
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic int read_log_page(struct ntfs_log *log, u32 vbo,
112462306a36Sopenharmony_ci			 struct RECORD_PAGE_HDR **buffer, bool *usa_error)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	int err = 0;
112762306a36Sopenharmony_ci	u32 page_idx = vbo >> log->page_bits;
112862306a36Sopenharmony_ci	u32 page_off = vbo & log->page_mask;
112962306a36Sopenharmony_ci	u32 bytes = log->page_size - page_off;
113062306a36Sopenharmony_ci	void *to_free = NULL;
113162306a36Sopenharmony_ci	u32 page_vbo = page_idx << log->page_bits;
113262306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *page_buf;
113362306a36Sopenharmony_ci	struct ntfs_inode *ni = log->ni;
113462306a36Sopenharmony_ci	bool bBAAD;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (vbo >= log->l_size)
113762306a36Sopenharmony_ci		return -EINVAL;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (!*buffer) {
114062306a36Sopenharmony_ci		to_free = kmalloc(log->page_size, GFP_NOFS);
114162306a36Sopenharmony_ci		if (!to_free)
114262306a36Sopenharmony_ci			return -ENOMEM;
114362306a36Sopenharmony_ci		*buffer = to_free;
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	page_buf = page_off ? log->one_page_buf : *buffer;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	err = ntfs_read_run_nb(ni->mi.sbi, &ni->file.run, page_vbo, page_buf,
114962306a36Sopenharmony_ci			       log->page_size, NULL);
115062306a36Sopenharmony_ci	if (err)
115162306a36Sopenharmony_ci		goto out;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (page_buf->rhdr.sign != NTFS_FFFF_SIGNATURE)
115462306a36Sopenharmony_ci		ntfs_fix_post_read(&page_buf->rhdr, PAGE_SIZE, false);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (page_buf != *buffer)
115762306a36Sopenharmony_ci		memcpy(*buffer, Add2Ptr(page_buf, page_off), bytes);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	bBAAD = page_buf->rhdr.sign == NTFS_BAAD_SIGNATURE;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (usa_error)
116262306a36Sopenharmony_ci		*usa_error = bBAAD;
116362306a36Sopenharmony_ci	/* Check that the update sequence array for this page is valid */
116462306a36Sopenharmony_ci	/* If we don't allow errors, raise an error status */
116562306a36Sopenharmony_ci	else if (bBAAD)
116662306a36Sopenharmony_ci		err = -EINVAL;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ciout:
116962306a36Sopenharmony_ci	if (err && to_free) {
117062306a36Sopenharmony_ci		kfree(to_free);
117162306a36Sopenharmony_ci		*buffer = NULL;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	return err;
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci/*
117862306a36Sopenharmony_ci * log_read_rst
117962306a36Sopenharmony_ci *
118062306a36Sopenharmony_ci * It walks through 512 blocks of the file looking for a valid
118162306a36Sopenharmony_ci * restart page header. It will stop the first time we find a
118262306a36Sopenharmony_ci * valid page header.
118362306a36Sopenharmony_ci */
118462306a36Sopenharmony_cistatic int log_read_rst(struct ntfs_log *log, bool first,
118562306a36Sopenharmony_ci			struct restart_info *info)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	u32 skip, vbo;
118862306a36Sopenharmony_ci	struct RESTART_HDR *r_page = NULL;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/* Determine which restart area we are looking for. */
119162306a36Sopenharmony_ci	if (first) {
119262306a36Sopenharmony_ci		vbo = 0;
119362306a36Sopenharmony_ci		skip = 512;
119462306a36Sopenharmony_ci	} else {
119562306a36Sopenharmony_ci		vbo = 512;
119662306a36Sopenharmony_ci		skip = 0;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	/* Loop continuously until we succeed. */
120062306a36Sopenharmony_ci	for (; vbo < log->l_size; vbo = 2 * vbo + skip, skip = 0) {
120162306a36Sopenharmony_ci		bool usa_error;
120262306a36Sopenharmony_ci		bool brst, bchk;
120362306a36Sopenharmony_ci		struct RESTART_AREA *ra;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		/* Read a page header at the current offset. */
120662306a36Sopenharmony_ci		if (read_log_page(log, vbo, (struct RECORD_PAGE_HDR **)&r_page,
120762306a36Sopenharmony_ci				  &usa_error)) {
120862306a36Sopenharmony_ci			/* Ignore any errors. */
120962306a36Sopenharmony_ci			continue;
121062306a36Sopenharmony_ci		}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		/* Exit if the signature is a log record page. */
121362306a36Sopenharmony_ci		if (r_page->rhdr.sign == NTFS_RCRD_SIGNATURE) {
121462306a36Sopenharmony_ci			info->initialized = true;
121562306a36Sopenharmony_ci			break;
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		brst = r_page->rhdr.sign == NTFS_RSTR_SIGNATURE;
121962306a36Sopenharmony_ci		bchk = r_page->rhdr.sign == NTFS_CHKD_SIGNATURE;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci		if (!bchk && !brst) {
122262306a36Sopenharmony_ci			if (r_page->rhdr.sign != NTFS_FFFF_SIGNATURE) {
122362306a36Sopenharmony_ci				/*
122462306a36Sopenharmony_ci				 * Remember if the signature does not
122562306a36Sopenharmony_ci				 * indicate uninitialized file.
122662306a36Sopenharmony_ci				 */
122762306a36Sopenharmony_ci				info->initialized = true;
122862306a36Sopenharmony_ci			}
122962306a36Sopenharmony_ci			continue;
123062306a36Sopenharmony_ci		}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci		ra = NULL;
123362306a36Sopenharmony_ci		info->valid_page = false;
123462306a36Sopenharmony_ci		info->initialized = true;
123562306a36Sopenharmony_ci		info->vbo = vbo;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		/* Let's check the restart area if this is a valid page. */
123862306a36Sopenharmony_ci		if (!is_rst_page_hdr_valid(vbo, r_page))
123962306a36Sopenharmony_ci			goto check_result;
124062306a36Sopenharmony_ci		ra = Add2Ptr(r_page, le16_to_cpu(r_page->ra_off));
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		if (!is_rst_area_valid(r_page))
124362306a36Sopenharmony_ci			goto check_result;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		/*
124662306a36Sopenharmony_ci		 * We have a valid restart page header and restart area.
124762306a36Sopenharmony_ci		 * If chkdsk was run or we have no clients then we have
124862306a36Sopenharmony_ci		 * no more checking to do.
124962306a36Sopenharmony_ci		 */
125062306a36Sopenharmony_ci		if (bchk || ra->client_idx[1] == LFS_NO_CLIENT_LE) {
125162306a36Sopenharmony_ci			info->valid_page = true;
125262306a36Sopenharmony_ci			goto check_result;
125362306a36Sopenharmony_ci		}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci		if (is_client_area_valid(r_page, usa_error)) {
125662306a36Sopenharmony_ci			info->valid_page = true;
125762306a36Sopenharmony_ci			ra = Add2Ptr(r_page, le16_to_cpu(r_page->ra_off));
125862306a36Sopenharmony_ci		}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cicheck_result:
126162306a36Sopenharmony_ci		/*
126262306a36Sopenharmony_ci		 * If chkdsk was run then update the caller's
126362306a36Sopenharmony_ci		 * values and return.
126462306a36Sopenharmony_ci		 */
126562306a36Sopenharmony_ci		if (r_page->rhdr.sign == NTFS_CHKD_SIGNATURE) {
126662306a36Sopenharmony_ci			info->chkdsk_was_run = true;
126762306a36Sopenharmony_ci			info->last_lsn = le64_to_cpu(r_page->rhdr.lsn);
126862306a36Sopenharmony_ci			info->restart = true;
126962306a36Sopenharmony_ci			info->r_page = r_page;
127062306a36Sopenharmony_ci			return 0;
127162306a36Sopenharmony_ci		}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		/*
127462306a36Sopenharmony_ci		 * If we have a valid page then copy the values
127562306a36Sopenharmony_ci		 * we need from it.
127662306a36Sopenharmony_ci		 */
127762306a36Sopenharmony_ci		if (info->valid_page) {
127862306a36Sopenharmony_ci			info->last_lsn = le64_to_cpu(ra->current_lsn);
127962306a36Sopenharmony_ci			info->restart = true;
128062306a36Sopenharmony_ci			info->r_page = r_page;
128162306a36Sopenharmony_ci			return 0;
128262306a36Sopenharmony_ci		}
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	kfree(r_page);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	return 0;
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci/*
129162306a36Sopenharmony_ci * Ilog_init_pg_hdr - Init @log from restart page header.
129262306a36Sopenharmony_ci */
129362306a36Sopenharmony_cistatic void log_init_pg_hdr(struct ntfs_log *log, u16 major_ver, u16 minor_ver)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	log->sys_page_size = log->page_size;
129662306a36Sopenharmony_ci	log->sys_page_mask = log->page_mask;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	log->clst_per_page = log->page_size >> log->ni->mi.sbi->cluster_bits;
129962306a36Sopenharmony_ci	if (!log->clst_per_page)
130062306a36Sopenharmony_ci		log->clst_per_page = 1;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	log->first_page = major_ver >= 2 ? 0x22 * log->page_size :
130362306a36Sopenharmony_ci					   4 * log->page_size;
130462306a36Sopenharmony_ci	log->major_ver = major_ver;
130562306a36Sopenharmony_ci	log->minor_ver = minor_ver;
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci/*
130962306a36Sopenharmony_ci * log_create - Init @log in cases when we don't have a restart area to use.
131062306a36Sopenharmony_ci */
131162306a36Sopenharmony_cistatic void log_create(struct ntfs_log *log, const u64 last_lsn,
131262306a36Sopenharmony_ci		       u32 open_log_count, bool wrapped, bool use_multi_page)
131362306a36Sopenharmony_ci{
131462306a36Sopenharmony_ci	/* All file offsets must be quadword aligned. */
131562306a36Sopenharmony_ci	log->file_data_bits = blksize_bits(log->l_size) - 3;
131662306a36Sopenharmony_ci	log->seq_num_mask = (8 << log->file_data_bits) - 1;
131762306a36Sopenharmony_ci	log->seq_num_bits = sizeof(u64) * 8 - log->file_data_bits;
131862306a36Sopenharmony_ci	log->seq_num = (last_lsn >> log->file_data_bits) + 2;
131962306a36Sopenharmony_ci	log->next_page = log->first_page;
132062306a36Sopenharmony_ci	log->oldest_lsn = log->seq_num << log->file_data_bits;
132162306a36Sopenharmony_ci	log->oldest_lsn_off = 0;
132262306a36Sopenharmony_ci	log->last_lsn = log->oldest_lsn;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	log->l_flags |= NTFSLOG_NO_LAST_LSN | NTFSLOG_NO_OLDEST_LSN;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/* Set the correct flags for the I/O and indicate if we have wrapped. */
132762306a36Sopenharmony_ci	if (wrapped)
132862306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_WRAPPED;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (use_multi_page)
133162306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_MULTIPLE_PAGE_IO;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/* Compute the log page values. */
133462306a36Sopenharmony_ci	log->data_off = ALIGN(
133562306a36Sopenharmony_ci		offsetof(struct RECORD_PAGE_HDR, fixups) +
133662306a36Sopenharmony_ci			sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1),
133762306a36Sopenharmony_ci		8);
133862306a36Sopenharmony_ci	log->data_size = log->page_size - log->data_off;
133962306a36Sopenharmony_ci	log->record_header_len = sizeof(struct LFS_RECORD_HDR);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	/* Remember the different page sizes for reservation. */
134262306a36Sopenharmony_ci	log->reserved = log->data_size - log->record_header_len;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	/* Compute the restart page values. */
134562306a36Sopenharmony_ci	log->ra_off = ALIGN(
134662306a36Sopenharmony_ci		offsetof(struct RESTART_HDR, fixups) +
134762306a36Sopenharmony_ci			sizeof(short) *
134862306a36Sopenharmony_ci				((log->sys_page_size >> SECTOR_SHIFT) + 1),
134962306a36Sopenharmony_ci		8);
135062306a36Sopenharmony_ci	log->restart_size = log->sys_page_size - log->ra_off;
135162306a36Sopenharmony_ci	log->ra_size = struct_size(log->ra, clients, 1);
135262306a36Sopenharmony_ci	log->current_openlog_count = open_log_count;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/*
135562306a36Sopenharmony_ci	 * The total available log file space is the number of
135662306a36Sopenharmony_ci	 * log file pages times the space available on each page.
135762306a36Sopenharmony_ci	 */
135862306a36Sopenharmony_ci	log->total_avail_pages = log->l_size - log->first_page;
135962306a36Sopenharmony_ci	log->total_avail = log->total_avail_pages >> log->page_bits;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/*
136262306a36Sopenharmony_ci	 * We assume that we can't use the end of the page less than
136362306a36Sopenharmony_ci	 * the file record size.
136462306a36Sopenharmony_ci	 * Then we won't need to reserve more than the caller asks for.
136562306a36Sopenharmony_ci	 */
136662306a36Sopenharmony_ci	log->max_current_avail = log->total_avail * log->reserved;
136762306a36Sopenharmony_ci	log->total_avail = log->total_avail * log->data_size;
136862306a36Sopenharmony_ci	log->current_avail = log->max_current_avail;
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci/*
137262306a36Sopenharmony_ci * log_create_ra - Fill a restart area from the values stored in @log.
137362306a36Sopenharmony_ci */
137462306a36Sopenharmony_cistatic struct RESTART_AREA *log_create_ra(struct ntfs_log *log)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	struct CLIENT_REC *cr;
137762306a36Sopenharmony_ci	struct RESTART_AREA *ra = kzalloc(log->restart_size, GFP_NOFS);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (!ra)
138062306a36Sopenharmony_ci		return NULL;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	ra->current_lsn = cpu_to_le64(log->last_lsn);
138362306a36Sopenharmony_ci	ra->log_clients = cpu_to_le16(1);
138462306a36Sopenharmony_ci	ra->client_idx[1] = LFS_NO_CLIENT_LE;
138562306a36Sopenharmony_ci	if (log->l_flags & NTFSLOG_MULTIPLE_PAGE_IO)
138662306a36Sopenharmony_ci		ra->flags = RESTART_SINGLE_PAGE_IO;
138762306a36Sopenharmony_ci	ra->seq_num_bits = cpu_to_le32(log->seq_num_bits);
138862306a36Sopenharmony_ci	ra->ra_len = cpu_to_le16(log->ra_size);
138962306a36Sopenharmony_ci	ra->client_off = cpu_to_le16(offsetof(struct RESTART_AREA, clients));
139062306a36Sopenharmony_ci	ra->l_size = cpu_to_le64(log->l_size);
139162306a36Sopenharmony_ci	ra->rec_hdr_len = cpu_to_le16(log->record_header_len);
139262306a36Sopenharmony_ci	ra->data_off = cpu_to_le16(log->data_off);
139362306a36Sopenharmony_ci	ra->open_log_count = cpu_to_le32(log->current_openlog_count + 1);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	cr = ra->clients;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	cr->prev_client = LFS_NO_CLIENT_LE;
139862306a36Sopenharmony_ci	cr->next_client = LFS_NO_CLIENT_LE;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	return ra;
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_cistatic u32 final_log_off(struct ntfs_log *log, u64 lsn, u32 data_len)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	u32 base_vbo = lsn << 3;
140662306a36Sopenharmony_ci	u32 final_log_off = (base_vbo & log->seq_num_mask) & ~log->page_mask;
140762306a36Sopenharmony_ci	u32 page_off = base_vbo & log->page_mask;
140862306a36Sopenharmony_ci	u32 tail = log->page_size - page_off;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	page_off -= 1;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	/* Add the length of the header. */
141362306a36Sopenharmony_ci	data_len += log->record_header_len;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	/*
141662306a36Sopenharmony_ci	 * If this lsn is contained this log page we are done.
141762306a36Sopenharmony_ci	 * Otherwise we need to walk through several log pages.
141862306a36Sopenharmony_ci	 */
141962306a36Sopenharmony_ci	if (data_len > tail) {
142062306a36Sopenharmony_ci		data_len -= tail;
142162306a36Sopenharmony_ci		tail = log->data_size;
142262306a36Sopenharmony_ci		page_off = log->data_off - 1;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		for (;;) {
142562306a36Sopenharmony_ci			final_log_off = next_page_off(log, final_log_off);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci			/*
142862306a36Sopenharmony_ci			 * We are done if the remaining bytes
142962306a36Sopenharmony_ci			 * fit on this page.
143062306a36Sopenharmony_ci			 */
143162306a36Sopenharmony_ci			if (data_len <= tail)
143262306a36Sopenharmony_ci				break;
143362306a36Sopenharmony_ci			data_len -= tail;
143462306a36Sopenharmony_ci		}
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	/*
143862306a36Sopenharmony_ci	 * We add the remaining bytes to our starting position on this page
143962306a36Sopenharmony_ci	 * and then add that value to the file offset of this log page.
144062306a36Sopenharmony_ci	 */
144162306a36Sopenharmony_ci	return final_log_off + data_len + page_off;
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh,
144562306a36Sopenharmony_ci			u64 *lsn)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	int err;
144862306a36Sopenharmony_ci	u64 this_lsn = le64_to_cpu(rh->this_lsn);
144962306a36Sopenharmony_ci	u32 vbo = lsn_to_vbo(log, this_lsn);
145062306a36Sopenharmony_ci	u32 end =
145162306a36Sopenharmony_ci		final_log_off(log, this_lsn, le32_to_cpu(rh->client_data_len));
145262306a36Sopenharmony_ci	u32 hdr_off = end & ~log->sys_page_mask;
145362306a36Sopenharmony_ci	u64 seq = this_lsn >> log->file_data_bits;
145462306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *page = NULL;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* Remember if we wrapped. */
145762306a36Sopenharmony_ci	if (end <= vbo)
145862306a36Sopenharmony_ci		seq += 1;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	/* Log page header for this page. */
146162306a36Sopenharmony_ci	err = read_log_page(log, hdr_off, &page, NULL);
146262306a36Sopenharmony_ci	if (err)
146362306a36Sopenharmony_ci		return err;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	/*
146662306a36Sopenharmony_ci	 * If the lsn we were given was not the last lsn on this page,
146762306a36Sopenharmony_ci	 * then the starting offset for the next lsn is on a quad word
146862306a36Sopenharmony_ci	 * boundary following the last file offset for the current lsn.
146962306a36Sopenharmony_ci	 * Otherwise the file offset is the start of the data on the next page.
147062306a36Sopenharmony_ci	 */
147162306a36Sopenharmony_ci	if (this_lsn == le64_to_cpu(page->rhdr.lsn)) {
147262306a36Sopenharmony_ci		/* If we wrapped, we need to increment the sequence number. */
147362306a36Sopenharmony_ci		hdr_off = next_page_off(log, hdr_off);
147462306a36Sopenharmony_ci		if (hdr_off == log->first_page)
147562306a36Sopenharmony_ci			seq += 1;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci		vbo = hdr_off + log->data_off;
147862306a36Sopenharmony_ci	} else {
147962306a36Sopenharmony_ci		vbo = ALIGN(end, 8);
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	/* Compute the lsn based on the file offset and the sequence count. */
148362306a36Sopenharmony_ci	*lsn = vbo_to_lsn(log, vbo, seq);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	/*
148662306a36Sopenharmony_ci	 * If this lsn is within the legal range for the file, we return true.
148762306a36Sopenharmony_ci	 * Otherwise false indicates that there are no more lsn's.
148862306a36Sopenharmony_ci	 */
148962306a36Sopenharmony_ci	if (!is_lsn_in_file(log, *lsn))
149062306a36Sopenharmony_ci		*lsn = 0;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	kfree(page);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	return 0;
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci/*
149862306a36Sopenharmony_ci * current_log_avail - Calculate the number of bytes available for log records.
149962306a36Sopenharmony_ci */
150062306a36Sopenharmony_cistatic u32 current_log_avail(struct ntfs_log *log)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	u32 oldest_off, next_free_off, free_bytes;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	if (log->l_flags & NTFSLOG_NO_LAST_LSN) {
150562306a36Sopenharmony_ci		/* The entire file is available. */
150662306a36Sopenharmony_ci		return log->max_current_avail;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	/*
151062306a36Sopenharmony_ci	 * If there is a last lsn the restart area then we know that we will
151162306a36Sopenharmony_ci	 * have to compute the free range.
151262306a36Sopenharmony_ci	 * If there is no oldest lsn then start at the first page of the file.
151362306a36Sopenharmony_ci	 */
151462306a36Sopenharmony_ci	oldest_off = (log->l_flags & NTFSLOG_NO_OLDEST_LSN) ?
151562306a36Sopenharmony_ci			     log->first_page :
151662306a36Sopenharmony_ci			     (log->oldest_lsn_off & ~log->sys_page_mask);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	/*
151962306a36Sopenharmony_ci	 * We will use the next log page offset to compute the next free page.
152062306a36Sopenharmony_ci	 * If we are going to reuse this page go to the next page.
152162306a36Sopenharmony_ci	 * If we are at the first page then use the end of the file.
152262306a36Sopenharmony_ci	 */
152362306a36Sopenharmony_ci	next_free_off = (log->l_flags & NTFSLOG_REUSE_TAIL) ?
152462306a36Sopenharmony_ci				log->next_page + log->page_size :
152562306a36Sopenharmony_ci			log->next_page == log->first_page ? log->l_size :
152662306a36Sopenharmony_ci							    log->next_page;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	/* If the two offsets are the same then there is no available space. */
152962306a36Sopenharmony_ci	if (oldest_off == next_free_off)
153062306a36Sopenharmony_ci		return 0;
153162306a36Sopenharmony_ci	/*
153262306a36Sopenharmony_ci	 * If the free offset follows the oldest offset then subtract
153362306a36Sopenharmony_ci	 * this range from the total available pages.
153462306a36Sopenharmony_ci	 */
153562306a36Sopenharmony_ci	free_bytes =
153662306a36Sopenharmony_ci		oldest_off < next_free_off ?
153762306a36Sopenharmony_ci			log->total_avail_pages - (next_free_off - oldest_off) :
153862306a36Sopenharmony_ci			oldest_off - next_free_off;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	free_bytes >>= log->page_bits;
154162306a36Sopenharmony_ci	return free_bytes * log->reserved;
154262306a36Sopenharmony_ci}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_cistatic bool check_subseq_log_page(struct ntfs_log *log,
154562306a36Sopenharmony_ci				  const struct RECORD_PAGE_HDR *rp, u32 vbo,
154662306a36Sopenharmony_ci				  u64 seq)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	u64 lsn_seq;
154962306a36Sopenharmony_ci	const struct NTFS_RECORD_HEADER *rhdr = &rp->rhdr;
155062306a36Sopenharmony_ci	u64 lsn = le64_to_cpu(rhdr->lsn);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	if (rhdr->sign == NTFS_FFFF_SIGNATURE || !rhdr->sign)
155362306a36Sopenharmony_ci		return false;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/*
155662306a36Sopenharmony_ci	 * If the last lsn on the page occurs was written after the page
155762306a36Sopenharmony_ci	 * that caused the original error then we have a fatal error.
155862306a36Sopenharmony_ci	 */
155962306a36Sopenharmony_ci	lsn_seq = lsn >> log->file_data_bits;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	/*
156262306a36Sopenharmony_ci	 * If the sequence number for the lsn the page is equal or greater
156362306a36Sopenharmony_ci	 * than lsn we expect, then this is a subsequent write.
156462306a36Sopenharmony_ci	 */
156562306a36Sopenharmony_ci	return lsn_seq >= seq ||
156662306a36Sopenharmony_ci	       (lsn_seq == seq - 1 && log->first_page == vbo &&
156762306a36Sopenharmony_ci		vbo != (lsn_to_vbo(log, lsn) & ~log->page_mask));
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci/*
157162306a36Sopenharmony_ci * last_log_lsn
157262306a36Sopenharmony_ci *
157362306a36Sopenharmony_ci * Walks through the log pages for a file, searching for the
157462306a36Sopenharmony_ci * last log page written to the file.
157562306a36Sopenharmony_ci */
157662306a36Sopenharmony_cistatic int last_log_lsn(struct ntfs_log *log)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci	int err;
157962306a36Sopenharmony_ci	bool usa_error = false;
158062306a36Sopenharmony_ci	bool replace_page = false;
158162306a36Sopenharmony_ci	bool reuse_page = log->l_flags & NTFSLOG_REUSE_TAIL;
158262306a36Sopenharmony_ci	bool wrapped_file, wrapped;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	u32 page_cnt = 1, page_pos = 1;
158562306a36Sopenharmony_ci	u32 page_off = 0, page_off1 = 0, saved_off = 0;
158662306a36Sopenharmony_ci	u32 final_off, second_off, final_off_prev = 0, second_off_prev = 0;
158762306a36Sopenharmony_ci	u32 first_file_off = 0, second_file_off = 0;
158862306a36Sopenharmony_ci	u32 part_io_count = 0;
158962306a36Sopenharmony_ci	u32 tails = 0;
159062306a36Sopenharmony_ci	u32 this_off, curpage_off, nextpage_off, remain_pages;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	u64 expected_seq, seq_base = 0, lsn_base = 0;
159362306a36Sopenharmony_ci	u64 best_lsn, best_lsn1, best_lsn2;
159462306a36Sopenharmony_ci	u64 lsn_cur, lsn1, lsn2;
159562306a36Sopenharmony_ci	u64 last_ok_lsn = reuse_page ? log->last_lsn : 0;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	u16 cur_pos, best_page_pos;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *page = NULL;
160062306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *tst_page = NULL;
160162306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *first_tail = NULL;
160262306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *second_tail = NULL;
160362306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *tail_page = NULL;
160462306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *second_tail_prev = NULL;
160562306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *first_tail_prev = NULL;
160662306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *page_bufs = NULL;
160762306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *best_page;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	if (log->major_ver >= 2) {
161062306a36Sopenharmony_ci		final_off = 0x02 * log->page_size;
161162306a36Sopenharmony_ci		second_off = 0x12 * log->page_size;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		// 0x10 == 0x12 - 0x2
161462306a36Sopenharmony_ci		page_bufs = kmalloc(log->page_size * 0x10, GFP_NOFS);
161562306a36Sopenharmony_ci		if (!page_bufs)
161662306a36Sopenharmony_ci			return -ENOMEM;
161762306a36Sopenharmony_ci	} else {
161862306a36Sopenharmony_ci		second_off = log->first_page - log->page_size;
161962306a36Sopenharmony_ci		final_off = second_off - log->page_size;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cinext_tail:
162362306a36Sopenharmony_ci	/* Read second tail page (at pos 3/0x12000). */
162462306a36Sopenharmony_ci	if (read_log_page(log, second_off, &second_tail, &usa_error) ||
162562306a36Sopenharmony_ci	    usa_error || second_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) {
162662306a36Sopenharmony_ci		kfree(second_tail);
162762306a36Sopenharmony_ci		second_tail = NULL;
162862306a36Sopenharmony_ci		second_file_off = 0;
162962306a36Sopenharmony_ci		lsn2 = 0;
163062306a36Sopenharmony_ci	} else {
163162306a36Sopenharmony_ci		second_file_off = hdr_file_off(log, second_tail);
163262306a36Sopenharmony_ci		lsn2 = le64_to_cpu(second_tail->record_hdr.last_end_lsn);
163362306a36Sopenharmony_ci	}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	/* Read first tail page (at pos 2/0x2000). */
163662306a36Sopenharmony_ci	if (read_log_page(log, final_off, &first_tail, &usa_error) ||
163762306a36Sopenharmony_ci	    usa_error || first_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) {
163862306a36Sopenharmony_ci		kfree(first_tail);
163962306a36Sopenharmony_ci		first_tail = NULL;
164062306a36Sopenharmony_ci		first_file_off = 0;
164162306a36Sopenharmony_ci		lsn1 = 0;
164262306a36Sopenharmony_ci	} else {
164362306a36Sopenharmony_ci		first_file_off = hdr_file_off(log, first_tail);
164462306a36Sopenharmony_ci		lsn1 = le64_to_cpu(first_tail->record_hdr.last_end_lsn);
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	if (log->major_ver < 2) {
164862306a36Sopenharmony_ci		int best_page;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci		first_tail_prev = first_tail;
165162306a36Sopenharmony_ci		final_off_prev = first_file_off;
165262306a36Sopenharmony_ci		second_tail_prev = second_tail;
165362306a36Sopenharmony_ci		second_off_prev = second_file_off;
165462306a36Sopenharmony_ci		tails = 1;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci		if (!first_tail && !second_tail)
165762306a36Sopenharmony_ci			goto tail_read;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci		if (first_tail && second_tail)
166062306a36Sopenharmony_ci			best_page = lsn1 < lsn2 ? 1 : 0;
166162306a36Sopenharmony_ci		else if (first_tail)
166262306a36Sopenharmony_ci			best_page = 0;
166362306a36Sopenharmony_ci		else
166462306a36Sopenharmony_ci			best_page = 1;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci		page_off = best_page ? second_file_off : first_file_off;
166762306a36Sopenharmony_ci		seq_base = (best_page ? lsn2 : lsn1) >> log->file_data_bits;
166862306a36Sopenharmony_ci		goto tail_read;
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	best_lsn1 = first_tail ? base_lsn(log, first_tail, first_file_off) : 0;
167262306a36Sopenharmony_ci	best_lsn2 = second_tail ? base_lsn(log, second_tail, second_file_off) :
167362306a36Sopenharmony_ci				  0;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (first_tail && second_tail) {
167662306a36Sopenharmony_ci		if (best_lsn1 > best_lsn2) {
167762306a36Sopenharmony_ci			best_lsn = best_lsn1;
167862306a36Sopenharmony_ci			best_page = first_tail;
167962306a36Sopenharmony_ci			this_off = first_file_off;
168062306a36Sopenharmony_ci		} else {
168162306a36Sopenharmony_ci			best_lsn = best_lsn2;
168262306a36Sopenharmony_ci			best_page = second_tail;
168362306a36Sopenharmony_ci			this_off = second_file_off;
168462306a36Sopenharmony_ci		}
168562306a36Sopenharmony_ci	} else if (first_tail) {
168662306a36Sopenharmony_ci		best_lsn = best_lsn1;
168762306a36Sopenharmony_ci		best_page = first_tail;
168862306a36Sopenharmony_ci		this_off = first_file_off;
168962306a36Sopenharmony_ci	} else if (second_tail) {
169062306a36Sopenharmony_ci		best_lsn = best_lsn2;
169162306a36Sopenharmony_ci		best_page = second_tail;
169262306a36Sopenharmony_ci		this_off = second_file_off;
169362306a36Sopenharmony_ci	} else {
169462306a36Sopenharmony_ci		goto tail_read;
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	best_page_pos = le16_to_cpu(best_page->page_pos);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	if (!tails) {
170062306a36Sopenharmony_ci		if (best_page_pos == page_pos) {
170162306a36Sopenharmony_ci			seq_base = best_lsn >> log->file_data_bits;
170262306a36Sopenharmony_ci			saved_off = page_off = le32_to_cpu(best_page->file_off);
170362306a36Sopenharmony_ci			lsn_base = best_lsn;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci			memmove(page_bufs, best_page, log->page_size);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci			page_cnt = le16_to_cpu(best_page->page_count);
170862306a36Sopenharmony_ci			if (page_cnt > 1)
170962306a36Sopenharmony_ci				page_pos += 1;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci			tails = 1;
171262306a36Sopenharmony_ci		}
171362306a36Sopenharmony_ci	} else if (seq_base == (best_lsn >> log->file_data_bits) &&
171462306a36Sopenharmony_ci		   saved_off + log->page_size == this_off &&
171562306a36Sopenharmony_ci		   lsn_base < best_lsn &&
171662306a36Sopenharmony_ci		   (page_pos != page_cnt || best_page_pos == page_pos ||
171762306a36Sopenharmony_ci		    best_page_pos == 1) &&
171862306a36Sopenharmony_ci		   (page_pos >= page_cnt || best_page_pos == page_pos)) {
171962306a36Sopenharmony_ci		u16 bppc = le16_to_cpu(best_page->page_count);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci		saved_off += log->page_size;
172262306a36Sopenharmony_ci		lsn_base = best_lsn;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci		memmove(Add2Ptr(page_bufs, tails * log->page_size), best_page,
172562306a36Sopenharmony_ci			log->page_size);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci		tails += 1;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci		if (best_page_pos != bppc) {
173062306a36Sopenharmony_ci			page_cnt = bppc;
173162306a36Sopenharmony_ci			page_pos = best_page_pos;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci			if (page_cnt > 1)
173462306a36Sopenharmony_ci				page_pos += 1;
173562306a36Sopenharmony_ci		} else {
173662306a36Sopenharmony_ci			page_pos = page_cnt = 1;
173762306a36Sopenharmony_ci		}
173862306a36Sopenharmony_ci	} else {
173962306a36Sopenharmony_ci		kfree(first_tail);
174062306a36Sopenharmony_ci		kfree(second_tail);
174162306a36Sopenharmony_ci		goto tail_read;
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	kfree(first_tail_prev);
174562306a36Sopenharmony_ci	first_tail_prev = first_tail;
174662306a36Sopenharmony_ci	final_off_prev = first_file_off;
174762306a36Sopenharmony_ci	first_tail = NULL;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	kfree(second_tail_prev);
175062306a36Sopenharmony_ci	second_tail_prev = second_tail;
175162306a36Sopenharmony_ci	second_off_prev = second_file_off;
175262306a36Sopenharmony_ci	second_tail = NULL;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	final_off += log->page_size;
175562306a36Sopenharmony_ci	second_off += log->page_size;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (tails < 0x10)
175862306a36Sopenharmony_ci		goto next_tail;
175962306a36Sopenharmony_citail_read:
176062306a36Sopenharmony_ci	first_tail = first_tail_prev;
176162306a36Sopenharmony_ci	final_off = final_off_prev;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	second_tail = second_tail_prev;
176462306a36Sopenharmony_ci	second_off = second_off_prev;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	page_cnt = page_pos = 1;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	curpage_off = seq_base == log->seq_num ? min(log->next_page, page_off) :
176962306a36Sopenharmony_ci						 log->next_page;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	wrapped_file =
177262306a36Sopenharmony_ci		curpage_off == log->first_page &&
177362306a36Sopenharmony_ci		!(log->l_flags & (NTFSLOG_NO_LAST_LSN | NTFSLOG_REUSE_TAIL));
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	expected_seq = wrapped_file ? (log->seq_num + 1) : log->seq_num;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	nextpage_off = curpage_off;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cinext_page:
178062306a36Sopenharmony_ci	tail_page = NULL;
178162306a36Sopenharmony_ci	/* Read the next log page. */
178262306a36Sopenharmony_ci	err = read_log_page(log, curpage_off, &page, &usa_error);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/* Compute the next log page offset the file. */
178562306a36Sopenharmony_ci	nextpage_off = next_page_off(log, curpage_off);
178662306a36Sopenharmony_ci	wrapped = nextpage_off == log->first_page;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	if (tails > 1) {
178962306a36Sopenharmony_ci		struct RECORD_PAGE_HDR *cur_page =
179062306a36Sopenharmony_ci			Add2Ptr(page_bufs, curpage_off - page_off);
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci		if (curpage_off == saved_off) {
179362306a36Sopenharmony_ci			tail_page = cur_page;
179462306a36Sopenharmony_ci			goto use_tail_page;
179562306a36Sopenharmony_ci		}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci		if (page_off > curpage_off || curpage_off >= saved_off)
179862306a36Sopenharmony_ci			goto use_tail_page;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci		if (page_off1)
180162306a36Sopenharmony_ci			goto use_cur_page;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci		if (!err && !usa_error &&
180462306a36Sopenharmony_ci		    page->rhdr.sign == NTFS_RCRD_SIGNATURE &&
180562306a36Sopenharmony_ci		    cur_page->rhdr.lsn == page->rhdr.lsn &&
180662306a36Sopenharmony_ci		    cur_page->record_hdr.next_record_off ==
180762306a36Sopenharmony_ci			    page->record_hdr.next_record_off &&
180862306a36Sopenharmony_ci		    ((page_pos == page_cnt &&
180962306a36Sopenharmony_ci		      le16_to_cpu(page->page_pos) == 1) ||
181062306a36Sopenharmony_ci		     (page_pos != page_cnt &&
181162306a36Sopenharmony_ci		      le16_to_cpu(page->page_pos) == page_pos + 1 &&
181262306a36Sopenharmony_ci		      le16_to_cpu(page->page_count) == page_cnt))) {
181362306a36Sopenharmony_ci			cur_page = NULL;
181462306a36Sopenharmony_ci			goto use_tail_page;
181562306a36Sopenharmony_ci		}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci		page_off1 = page_off;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ciuse_cur_page:
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci		lsn_cur = le64_to_cpu(cur_page->rhdr.lsn);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci		if (last_ok_lsn !=
182462306a36Sopenharmony_ci			    le64_to_cpu(cur_page->record_hdr.last_end_lsn) &&
182562306a36Sopenharmony_ci		    ((lsn_cur >> log->file_data_bits) +
182662306a36Sopenharmony_ci		     ((curpage_off <
182762306a36Sopenharmony_ci		       (lsn_to_vbo(log, lsn_cur) & ~log->page_mask)) ?
182862306a36Sopenharmony_ci			      1 :
182962306a36Sopenharmony_ci			      0)) != expected_seq) {
183062306a36Sopenharmony_ci			goto check_tail;
183162306a36Sopenharmony_ci		}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci		if (!is_log_record_end(cur_page)) {
183462306a36Sopenharmony_ci			tail_page = NULL;
183562306a36Sopenharmony_ci			last_ok_lsn = lsn_cur;
183662306a36Sopenharmony_ci			goto next_page_1;
183762306a36Sopenharmony_ci		}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		log->seq_num = expected_seq;
184062306a36Sopenharmony_ci		log->l_flags &= ~NTFSLOG_NO_LAST_LSN;
184162306a36Sopenharmony_ci		log->last_lsn = le64_to_cpu(cur_page->record_hdr.last_end_lsn);
184262306a36Sopenharmony_ci		log->ra->current_lsn = cur_page->record_hdr.last_end_lsn;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci		if (log->record_header_len <=
184562306a36Sopenharmony_ci		    log->page_size -
184662306a36Sopenharmony_ci			    le16_to_cpu(cur_page->record_hdr.next_record_off)) {
184762306a36Sopenharmony_ci			log->l_flags |= NTFSLOG_REUSE_TAIL;
184862306a36Sopenharmony_ci			log->next_page = curpage_off;
184962306a36Sopenharmony_ci		} else {
185062306a36Sopenharmony_ci			log->l_flags &= ~NTFSLOG_REUSE_TAIL;
185162306a36Sopenharmony_ci			log->next_page = nextpage_off;
185262306a36Sopenharmony_ci		}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci		if (wrapped_file)
185562306a36Sopenharmony_ci			log->l_flags |= NTFSLOG_WRAPPED;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci		last_ok_lsn = le64_to_cpu(cur_page->record_hdr.last_end_lsn);
185862306a36Sopenharmony_ci		goto next_page_1;
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	/*
186262306a36Sopenharmony_ci	 * If we are at the expected first page of a transfer check to see
186362306a36Sopenharmony_ci	 * if either tail copy is at this offset.
186462306a36Sopenharmony_ci	 * If this page is the last page of a transfer, check if we wrote
186562306a36Sopenharmony_ci	 * a subsequent tail copy.
186662306a36Sopenharmony_ci	 */
186762306a36Sopenharmony_ci	if (page_cnt == page_pos || page_cnt == page_pos + 1) {
186862306a36Sopenharmony_ci		/*
186962306a36Sopenharmony_ci		 * Check if the offset matches either the first or second
187062306a36Sopenharmony_ci		 * tail copy. It is possible it will match both.
187162306a36Sopenharmony_ci		 */
187262306a36Sopenharmony_ci		if (curpage_off == final_off)
187362306a36Sopenharmony_ci			tail_page = first_tail;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci		/*
187662306a36Sopenharmony_ci		 * If we already matched on the first page then
187762306a36Sopenharmony_ci		 * check the ending lsn's.
187862306a36Sopenharmony_ci		 */
187962306a36Sopenharmony_ci		if (curpage_off == second_off) {
188062306a36Sopenharmony_ci			if (!tail_page ||
188162306a36Sopenharmony_ci			    (second_tail &&
188262306a36Sopenharmony_ci			     le64_to_cpu(second_tail->record_hdr.last_end_lsn) >
188362306a36Sopenharmony_ci				     le64_to_cpu(first_tail->record_hdr
188462306a36Sopenharmony_ci							 .last_end_lsn))) {
188562306a36Sopenharmony_ci				tail_page = second_tail;
188662306a36Sopenharmony_ci			}
188762306a36Sopenharmony_ci		}
188862306a36Sopenharmony_ci	}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ciuse_tail_page:
189162306a36Sopenharmony_ci	if (tail_page) {
189262306a36Sopenharmony_ci		/* We have a candidate for a tail copy. */
189362306a36Sopenharmony_ci		lsn_cur = le64_to_cpu(tail_page->record_hdr.last_end_lsn);
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci		if (last_ok_lsn < lsn_cur) {
189662306a36Sopenharmony_ci			/*
189762306a36Sopenharmony_ci			 * If the sequence number is not expected,
189862306a36Sopenharmony_ci			 * then don't use the tail copy.
189962306a36Sopenharmony_ci			 */
190062306a36Sopenharmony_ci			if (expected_seq != (lsn_cur >> log->file_data_bits))
190162306a36Sopenharmony_ci				tail_page = NULL;
190262306a36Sopenharmony_ci		} else if (last_ok_lsn > lsn_cur) {
190362306a36Sopenharmony_ci			/*
190462306a36Sopenharmony_ci			 * If the last lsn is greater than the one on
190562306a36Sopenharmony_ci			 * this page then forget this tail.
190662306a36Sopenharmony_ci			 */
190762306a36Sopenharmony_ci			tail_page = NULL;
190862306a36Sopenharmony_ci		}
190962306a36Sopenharmony_ci	}
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	/*
191262306a36Sopenharmony_ci	 *If we have an error on the current page,
191362306a36Sopenharmony_ci	 * we will break of this loop.
191462306a36Sopenharmony_ci	 */
191562306a36Sopenharmony_ci	if (err || usa_error)
191662306a36Sopenharmony_ci		goto check_tail;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	/*
191962306a36Sopenharmony_ci	 * Done if the last lsn on this page doesn't match the previous known
192062306a36Sopenharmony_ci	 * last lsn or the sequence number is not expected.
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	lsn_cur = le64_to_cpu(page->rhdr.lsn);
192362306a36Sopenharmony_ci	if (last_ok_lsn != lsn_cur &&
192462306a36Sopenharmony_ci	    expected_seq != (lsn_cur >> log->file_data_bits)) {
192562306a36Sopenharmony_ci		goto check_tail;
192662306a36Sopenharmony_ci	}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	/*
192962306a36Sopenharmony_ci	 * Check that the page position and page count values are correct.
193062306a36Sopenharmony_ci	 * If this is the first page of a transfer the position must be 1
193162306a36Sopenharmony_ci	 * and the count will be unknown.
193262306a36Sopenharmony_ci	 */
193362306a36Sopenharmony_ci	if (page_cnt == page_pos) {
193462306a36Sopenharmony_ci		if (page->page_pos != cpu_to_le16(1) &&
193562306a36Sopenharmony_ci		    (!reuse_page || page->page_pos != page->page_count)) {
193662306a36Sopenharmony_ci			/*
193762306a36Sopenharmony_ci			 * If the current page is the first page we are
193862306a36Sopenharmony_ci			 * looking at and we are reusing this page then
193962306a36Sopenharmony_ci			 * it can be either the first or last page of a
194062306a36Sopenharmony_ci			 * transfer. Otherwise it can only be the first.
194162306a36Sopenharmony_ci			 */
194262306a36Sopenharmony_ci			goto check_tail;
194362306a36Sopenharmony_ci		}
194462306a36Sopenharmony_ci	} else if (le16_to_cpu(page->page_count) != page_cnt ||
194562306a36Sopenharmony_ci		   le16_to_cpu(page->page_pos) != page_pos + 1) {
194662306a36Sopenharmony_ci		/*
194762306a36Sopenharmony_ci		 * The page position better be 1 more than the last page
194862306a36Sopenharmony_ci		 * position and the page count better match.
194962306a36Sopenharmony_ci		 */
195062306a36Sopenharmony_ci		goto check_tail;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	/*
195462306a36Sopenharmony_ci	 * We have a valid page the file and may have a valid page
195562306a36Sopenharmony_ci	 * the tail copy area.
195662306a36Sopenharmony_ci	 * If the tail page was written after the page the file then
195762306a36Sopenharmony_ci	 * break of the loop.
195862306a36Sopenharmony_ci	 */
195962306a36Sopenharmony_ci	if (tail_page &&
196062306a36Sopenharmony_ci	    le64_to_cpu(tail_page->record_hdr.last_end_lsn) > lsn_cur) {
196162306a36Sopenharmony_ci		/* Remember if we will replace the page. */
196262306a36Sopenharmony_ci		replace_page = true;
196362306a36Sopenharmony_ci		goto check_tail;
196462306a36Sopenharmony_ci	}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	tail_page = NULL;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	if (is_log_record_end(page)) {
196962306a36Sopenharmony_ci		/*
197062306a36Sopenharmony_ci		 * Since we have read this page we know the sequence number
197162306a36Sopenharmony_ci		 * is the same as our expected value.
197262306a36Sopenharmony_ci		 */
197362306a36Sopenharmony_ci		log->seq_num = expected_seq;
197462306a36Sopenharmony_ci		log->last_lsn = le64_to_cpu(page->record_hdr.last_end_lsn);
197562306a36Sopenharmony_ci		log->ra->current_lsn = page->record_hdr.last_end_lsn;
197662306a36Sopenharmony_ci		log->l_flags &= ~NTFSLOG_NO_LAST_LSN;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci		/*
197962306a36Sopenharmony_ci		 * If there is room on this page for another header then
198062306a36Sopenharmony_ci		 * remember we want to reuse the page.
198162306a36Sopenharmony_ci		 */
198262306a36Sopenharmony_ci		if (log->record_header_len <=
198362306a36Sopenharmony_ci		    log->page_size -
198462306a36Sopenharmony_ci			    le16_to_cpu(page->record_hdr.next_record_off)) {
198562306a36Sopenharmony_ci			log->l_flags |= NTFSLOG_REUSE_TAIL;
198662306a36Sopenharmony_ci			log->next_page = curpage_off;
198762306a36Sopenharmony_ci		} else {
198862306a36Sopenharmony_ci			log->l_flags &= ~NTFSLOG_REUSE_TAIL;
198962306a36Sopenharmony_ci			log->next_page = nextpage_off;
199062306a36Sopenharmony_ci		}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci		/* Remember if we wrapped the log file. */
199362306a36Sopenharmony_ci		if (wrapped_file)
199462306a36Sopenharmony_ci			log->l_flags |= NTFSLOG_WRAPPED;
199562306a36Sopenharmony_ci	}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	/*
199862306a36Sopenharmony_ci	 * Remember the last page count and position.
199962306a36Sopenharmony_ci	 * Also remember the last known lsn.
200062306a36Sopenharmony_ci	 */
200162306a36Sopenharmony_ci	page_cnt = le16_to_cpu(page->page_count);
200262306a36Sopenharmony_ci	page_pos = le16_to_cpu(page->page_pos);
200362306a36Sopenharmony_ci	last_ok_lsn = le64_to_cpu(page->rhdr.lsn);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cinext_page_1:
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	if (wrapped) {
200862306a36Sopenharmony_ci		expected_seq += 1;
200962306a36Sopenharmony_ci		wrapped_file = 1;
201062306a36Sopenharmony_ci	}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	curpage_off = nextpage_off;
201362306a36Sopenharmony_ci	kfree(page);
201462306a36Sopenharmony_ci	page = NULL;
201562306a36Sopenharmony_ci	reuse_page = 0;
201662306a36Sopenharmony_ci	goto next_page;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cicheck_tail:
201962306a36Sopenharmony_ci	if (tail_page) {
202062306a36Sopenharmony_ci		log->seq_num = expected_seq;
202162306a36Sopenharmony_ci		log->last_lsn = le64_to_cpu(tail_page->record_hdr.last_end_lsn);
202262306a36Sopenharmony_ci		log->ra->current_lsn = tail_page->record_hdr.last_end_lsn;
202362306a36Sopenharmony_ci		log->l_flags &= ~NTFSLOG_NO_LAST_LSN;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci		if (log->page_size -
202662306a36Sopenharmony_ci			    le16_to_cpu(
202762306a36Sopenharmony_ci				    tail_page->record_hdr.next_record_off) >=
202862306a36Sopenharmony_ci		    log->record_header_len) {
202962306a36Sopenharmony_ci			log->l_flags |= NTFSLOG_REUSE_TAIL;
203062306a36Sopenharmony_ci			log->next_page = curpage_off;
203162306a36Sopenharmony_ci		} else {
203262306a36Sopenharmony_ci			log->l_flags &= ~NTFSLOG_REUSE_TAIL;
203362306a36Sopenharmony_ci			log->next_page = nextpage_off;
203462306a36Sopenharmony_ci		}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci		if (wrapped)
203762306a36Sopenharmony_ci			log->l_flags |= NTFSLOG_WRAPPED;
203862306a36Sopenharmony_ci	}
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	/* Remember that the partial IO will start at the next page. */
204162306a36Sopenharmony_ci	second_off = nextpage_off;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	/*
204462306a36Sopenharmony_ci	 * If the next page is the first page of the file then update
204562306a36Sopenharmony_ci	 * the sequence number for log records which begon the next page.
204662306a36Sopenharmony_ci	 */
204762306a36Sopenharmony_ci	if (wrapped)
204862306a36Sopenharmony_ci		expected_seq += 1;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	/*
205162306a36Sopenharmony_ci	 * If we have a tail copy or are performing single page I/O we can
205262306a36Sopenharmony_ci	 * immediately look at the next page.
205362306a36Sopenharmony_ci	 */
205462306a36Sopenharmony_ci	if (replace_page || (log->ra->flags & RESTART_SINGLE_PAGE_IO)) {
205562306a36Sopenharmony_ci		page_cnt = 2;
205662306a36Sopenharmony_ci		page_pos = 1;
205762306a36Sopenharmony_ci		goto check_valid;
205862306a36Sopenharmony_ci	}
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	if (page_pos != page_cnt)
206162306a36Sopenharmony_ci		goto check_valid;
206262306a36Sopenharmony_ci	/*
206362306a36Sopenharmony_ci	 * If the next page causes us to wrap to the beginning of the log
206462306a36Sopenharmony_ci	 * file then we know which page to check next.
206562306a36Sopenharmony_ci	 */
206662306a36Sopenharmony_ci	if (wrapped) {
206762306a36Sopenharmony_ci		page_cnt = 2;
206862306a36Sopenharmony_ci		page_pos = 1;
206962306a36Sopenharmony_ci		goto check_valid;
207062306a36Sopenharmony_ci	}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	cur_pos = 2;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_cinext_test_page:
207562306a36Sopenharmony_ci	kfree(tst_page);
207662306a36Sopenharmony_ci	tst_page = NULL;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	/* Walk through the file, reading log pages. */
207962306a36Sopenharmony_ci	err = read_log_page(log, nextpage_off, &tst_page, &usa_error);
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	/*
208262306a36Sopenharmony_ci	 * If we get a USA error then assume that we correctly found
208362306a36Sopenharmony_ci	 * the end of the original transfer.
208462306a36Sopenharmony_ci	 */
208562306a36Sopenharmony_ci	if (usa_error)
208662306a36Sopenharmony_ci		goto file_is_valid;
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	/*
208962306a36Sopenharmony_ci	 * If we were able to read the page, we examine it to see if it
209062306a36Sopenharmony_ci	 * is the same or different Io block.
209162306a36Sopenharmony_ci	 */
209262306a36Sopenharmony_ci	if (err)
209362306a36Sopenharmony_ci		goto next_test_page_1;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	if (le16_to_cpu(tst_page->page_pos) == cur_pos &&
209662306a36Sopenharmony_ci	    check_subseq_log_page(log, tst_page, nextpage_off, expected_seq)) {
209762306a36Sopenharmony_ci		page_cnt = le16_to_cpu(tst_page->page_count) + 1;
209862306a36Sopenharmony_ci		page_pos = le16_to_cpu(tst_page->page_pos);
209962306a36Sopenharmony_ci		goto check_valid;
210062306a36Sopenharmony_ci	} else {
210162306a36Sopenharmony_ci		goto file_is_valid;
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_cinext_test_page_1:
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	nextpage_off = next_page_off(log, curpage_off);
210762306a36Sopenharmony_ci	wrapped = nextpage_off == log->first_page;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	if (wrapped) {
211062306a36Sopenharmony_ci		expected_seq += 1;
211162306a36Sopenharmony_ci		page_cnt = 2;
211262306a36Sopenharmony_ci		page_pos = 1;
211362306a36Sopenharmony_ci	}
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	cur_pos += 1;
211662306a36Sopenharmony_ci	part_io_count += 1;
211762306a36Sopenharmony_ci	if (!wrapped)
211862306a36Sopenharmony_ci		goto next_test_page;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_cicheck_valid:
212162306a36Sopenharmony_ci	/* Skip over the remaining pages this transfer. */
212262306a36Sopenharmony_ci	remain_pages = page_cnt - page_pos - 1;
212362306a36Sopenharmony_ci	part_io_count += remain_pages;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	while (remain_pages--) {
212662306a36Sopenharmony_ci		nextpage_off = next_page_off(log, curpage_off);
212762306a36Sopenharmony_ci		wrapped = nextpage_off == log->first_page;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci		if (wrapped)
213062306a36Sopenharmony_ci			expected_seq += 1;
213162306a36Sopenharmony_ci	}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	/* Call our routine to check this log page. */
213462306a36Sopenharmony_ci	kfree(tst_page);
213562306a36Sopenharmony_ci	tst_page = NULL;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	err = read_log_page(log, nextpage_off, &tst_page, &usa_error);
213862306a36Sopenharmony_ci	if (!err && !usa_error &&
213962306a36Sopenharmony_ci	    check_subseq_log_page(log, tst_page, nextpage_off, expected_seq)) {
214062306a36Sopenharmony_ci		err = -EINVAL;
214162306a36Sopenharmony_ci		goto out;
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cifile_is_valid:
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	/* We have a valid file. */
214762306a36Sopenharmony_ci	if (page_off1 || tail_page) {
214862306a36Sopenharmony_ci		struct RECORD_PAGE_HDR *tmp_page;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci		if (sb_rdonly(log->ni->mi.sbi->sb)) {
215162306a36Sopenharmony_ci			err = -EROFS;
215262306a36Sopenharmony_ci			goto out;
215362306a36Sopenharmony_ci		}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci		if (page_off1) {
215662306a36Sopenharmony_ci			tmp_page = Add2Ptr(page_bufs, page_off1 - page_off);
215762306a36Sopenharmony_ci			tails -= (page_off1 - page_off) / log->page_size;
215862306a36Sopenharmony_ci			if (!tail_page)
215962306a36Sopenharmony_ci				tails -= 1;
216062306a36Sopenharmony_ci		} else {
216162306a36Sopenharmony_ci			tmp_page = tail_page;
216262306a36Sopenharmony_ci			tails = 1;
216362306a36Sopenharmony_ci		}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci		while (tails--) {
216662306a36Sopenharmony_ci			u64 off = hdr_file_off(log, tmp_page);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci			if (!page) {
216962306a36Sopenharmony_ci				page = kmalloc(log->page_size, GFP_NOFS);
217062306a36Sopenharmony_ci				if (!page) {
217162306a36Sopenharmony_ci					err = -ENOMEM;
217262306a36Sopenharmony_ci					goto out;
217362306a36Sopenharmony_ci				}
217462306a36Sopenharmony_ci			}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci			/*
217762306a36Sopenharmony_ci			 * Correct page and copy the data from this page
217862306a36Sopenharmony_ci			 * into it and flush it to disk.
217962306a36Sopenharmony_ci			 */
218062306a36Sopenharmony_ci			memcpy(page, tmp_page, log->page_size);
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci			/* Fill last flushed lsn value flush the page. */
218362306a36Sopenharmony_ci			if (log->major_ver < 2)
218462306a36Sopenharmony_ci				page->rhdr.lsn = page->record_hdr.last_end_lsn;
218562306a36Sopenharmony_ci			else
218662306a36Sopenharmony_ci				page->file_off = 0;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci			page->page_pos = page->page_count = cpu_to_le16(1);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci			ntfs_fix_pre_write(&page->rhdr, log->page_size);
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci			err = ntfs_sb_write_run(log->ni->mi.sbi,
219362306a36Sopenharmony_ci						&log->ni->file.run, off, page,
219462306a36Sopenharmony_ci						log->page_size, 0);
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci			if (err)
219762306a36Sopenharmony_ci				goto out;
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci			if (part_io_count && second_off == off) {
220062306a36Sopenharmony_ci				second_off += log->page_size;
220162306a36Sopenharmony_ci				part_io_count -= 1;
220262306a36Sopenharmony_ci			}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci			tmp_page = Add2Ptr(tmp_page, log->page_size);
220562306a36Sopenharmony_ci		}
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	if (part_io_count) {
220962306a36Sopenharmony_ci		if (sb_rdonly(log->ni->mi.sbi->sb)) {
221062306a36Sopenharmony_ci			err = -EROFS;
221162306a36Sopenharmony_ci			goto out;
221262306a36Sopenharmony_ci		}
221362306a36Sopenharmony_ci	}
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ciout:
221662306a36Sopenharmony_ci	kfree(second_tail);
221762306a36Sopenharmony_ci	kfree(first_tail);
221862306a36Sopenharmony_ci	kfree(page);
221962306a36Sopenharmony_ci	kfree(tst_page);
222062306a36Sopenharmony_ci	kfree(page_bufs);
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	return err;
222362306a36Sopenharmony_ci}
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci/*
222662306a36Sopenharmony_ci * read_log_rec_buf - Copy a log record from the file to a buffer.
222762306a36Sopenharmony_ci *
222862306a36Sopenharmony_ci * The log record may span several log pages and may even wrap the file.
222962306a36Sopenharmony_ci */
223062306a36Sopenharmony_cistatic int read_log_rec_buf(struct ntfs_log *log,
223162306a36Sopenharmony_ci			    const struct LFS_RECORD_HDR *rh, void *buffer)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	int err;
223462306a36Sopenharmony_ci	struct RECORD_PAGE_HDR *ph = NULL;
223562306a36Sopenharmony_ci	u64 lsn = le64_to_cpu(rh->this_lsn);
223662306a36Sopenharmony_ci	u32 vbo = lsn_to_vbo(log, lsn) & ~log->page_mask;
223762306a36Sopenharmony_ci	u32 off = lsn_to_page_off(log, lsn) + log->record_header_len;
223862306a36Sopenharmony_ci	u32 data_len = le32_to_cpu(rh->client_data_len);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	/*
224162306a36Sopenharmony_ci	 * While there are more bytes to transfer,
224262306a36Sopenharmony_ci	 * we continue to attempt to perform the read.
224362306a36Sopenharmony_ci	 */
224462306a36Sopenharmony_ci	for (;;) {
224562306a36Sopenharmony_ci		bool usa_error;
224662306a36Sopenharmony_ci		u32 tail = log->page_size - off;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci		if (tail >= data_len)
224962306a36Sopenharmony_ci			tail = data_len;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci		data_len -= tail;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci		err = read_log_page(log, vbo, &ph, &usa_error);
225462306a36Sopenharmony_ci		if (err)
225562306a36Sopenharmony_ci			goto out;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		/*
225862306a36Sopenharmony_ci		 * The last lsn on this page better be greater or equal
225962306a36Sopenharmony_ci		 * to the lsn we are copying.
226062306a36Sopenharmony_ci		 */
226162306a36Sopenharmony_ci		if (lsn > le64_to_cpu(ph->rhdr.lsn)) {
226262306a36Sopenharmony_ci			err = -EINVAL;
226362306a36Sopenharmony_ci			goto out;
226462306a36Sopenharmony_ci		}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci		memcpy(buffer, Add2Ptr(ph, off), tail);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci		/* If there are no more bytes to transfer, we exit the loop. */
226962306a36Sopenharmony_ci		if (!data_len) {
227062306a36Sopenharmony_ci			if (!is_log_record_end(ph) ||
227162306a36Sopenharmony_ci			    lsn > le64_to_cpu(ph->record_hdr.last_end_lsn)) {
227262306a36Sopenharmony_ci				err = -EINVAL;
227362306a36Sopenharmony_ci				goto out;
227462306a36Sopenharmony_ci			}
227562306a36Sopenharmony_ci			break;
227662306a36Sopenharmony_ci		}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci		if (ph->rhdr.lsn == ph->record_hdr.last_end_lsn ||
227962306a36Sopenharmony_ci		    lsn > le64_to_cpu(ph->rhdr.lsn)) {
228062306a36Sopenharmony_ci			err = -EINVAL;
228162306a36Sopenharmony_ci			goto out;
228262306a36Sopenharmony_ci		}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci		vbo = next_page_off(log, vbo);
228562306a36Sopenharmony_ci		off = log->data_off;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci		/*
228862306a36Sopenharmony_ci		 * Adjust our pointer the user's buffer to transfer
228962306a36Sopenharmony_ci		 * the next block to.
229062306a36Sopenharmony_ci		 */
229162306a36Sopenharmony_ci		buffer = Add2Ptr(buffer, tail);
229262306a36Sopenharmony_ci	}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ciout:
229562306a36Sopenharmony_ci	kfree(ph);
229662306a36Sopenharmony_ci	return err;
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_cistatic int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_,
230062306a36Sopenharmony_ci			 u64 *lsn)
230162306a36Sopenharmony_ci{
230262306a36Sopenharmony_ci	int err;
230362306a36Sopenharmony_ci	struct LFS_RECORD_HDR *rh = NULL;
230462306a36Sopenharmony_ci	const struct CLIENT_REC *cr =
230562306a36Sopenharmony_ci		Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off));
230662306a36Sopenharmony_ci	u64 lsnr, lsnc = le64_to_cpu(cr->restart_lsn);
230762306a36Sopenharmony_ci	u32 len;
230862306a36Sopenharmony_ci	struct NTFS_RESTART *rst;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	*lsn = 0;
231162306a36Sopenharmony_ci	*rst_ = NULL;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	/* If the client doesn't have a restart area, go ahead and exit now. */
231462306a36Sopenharmony_ci	if (!lsnc)
231562306a36Sopenharmony_ci		return 0;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	err = read_log_page(log, lsn_to_vbo(log, lsnc),
231862306a36Sopenharmony_ci			    (struct RECORD_PAGE_HDR **)&rh, NULL);
231962306a36Sopenharmony_ci	if (err)
232062306a36Sopenharmony_ci		return err;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci	rst = NULL;
232362306a36Sopenharmony_ci	lsnr = le64_to_cpu(rh->this_lsn);
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	if (lsnc != lsnr) {
232662306a36Sopenharmony_ci		/* If the lsn values don't match, then the disk is corrupt. */
232762306a36Sopenharmony_ci		err = -EINVAL;
232862306a36Sopenharmony_ci		goto out;
232962306a36Sopenharmony_ci	}
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	*lsn = lsnr;
233262306a36Sopenharmony_ci	len = le32_to_cpu(rh->client_data_len);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	if (!len) {
233562306a36Sopenharmony_ci		err = 0;
233662306a36Sopenharmony_ci		goto out;
233762306a36Sopenharmony_ci	}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	if (len < sizeof(struct NTFS_RESTART)) {
234062306a36Sopenharmony_ci		err = -EINVAL;
234162306a36Sopenharmony_ci		goto out;
234262306a36Sopenharmony_ci	}
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	rst = kmalloc(len, GFP_NOFS);
234562306a36Sopenharmony_ci	if (!rst) {
234662306a36Sopenharmony_ci		err = -ENOMEM;
234762306a36Sopenharmony_ci		goto out;
234862306a36Sopenharmony_ci	}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	/* Copy the data into the 'rst' buffer. */
235162306a36Sopenharmony_ci	err = read_log_rec_buf(log, rh, rst);
235262306a36Sopenharmony_ci	if (err)
235362306a36Sopenharmony_ci		goto out;
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	*rst_ = rst;
235662306a36Sopenharmony_ci	rst = NULL;
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ciout:
235962306a36Sopenharmony_ci	kfree(rh);
236062306a36Sopenharmony_ci	kfree(rst);
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	return err;
236362306a36Sopenharmony_ci}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_cistatic int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb)
236662306a36Sopenharmony_ci{
236762306a36Sopenharmony_ci	int err;
236862306a36Sopenharmony_ci	struct LFS_RECORD_HDR *rh = lcb->lrh;
236962306a36Sopenharmony_ci	u32 rec_len, len;
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	/* Read the record header for this lsn. */
237262306a36Sopenharmony_ci	if (!rh) {
237362306a36Sopenharmony_ci		err = read_log_page(log, lsn_to_vbo(log, lsn),
237462306a36Sopenharmony_ci				    (struct RECORD_PAGE_HDR **)&rh, NULL);
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci		lcb->lrh = rh;
237762306a36Sopenharmony_ci		if (err)
237862306a36Sopenharmony_ci			return err;
237962306a36Sopenharmony_ci	}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	/*
238262306a36Sopenharmony_ci	 * If the lsn the log record doesn't match the desired
238362306a36Sopenharmony_ci	 * lsn then the disk is corrupt.
238462306a36Sopenharmony_ci	 */
238562306a36Sopenharmony_ci	if (lsn != le64_to_cpu(rh->this_lsn))
238662306a36Sopenharmony_ci		return -EINVAL;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	len = le32_to_cpu(rh->client_data_len);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	/*
239162306a36Sopenharmony_ci	 * Check that the length field isn't greater than the total
239262306a36Sopenharmony_ci	 * available space the log file.
239362306a36Sopenharmony_ci	 */
239462306a36Sopenharmony_ci	rec_len = len + log->record_header_len;
239562306a36Sopenharmony_ci	if (rec_len >= log->total_avail)
239662306a36Sopenharmony_ci		return -EINVAL;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	/*
239962306a36Sopenharmony_ci	 * If the entire log record is on this log page,
240062306a36Sopenharmony_ci	 * put a pointer to the log record the context block.
240162306a36Sopenharmony_ci	 */
240262306a36Sopenharmony_ci	if (rh->flags & LOG_RECORD_MULTI_PAGE) {
240362306a36Sopenharmony_ci		void *lr = kmalloc(len, GFP_NOFS);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci		if (!lr)
240662306a36Sopenharmony_ci			return -ENOMEM;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci		lcb->log_rec = lr;
240962306a36Sopenharmony_ci		lcb->alloc = true;
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci		/* Copy the data into the buffer returned. */
241262306a36Sopenharmony_ci		err = read_log_rec_buf(log, rh, lr);
241362306a36Sopenharmony_ci		if (err)
241462306a36Sopenharmony_ci			return err;
241562306a36Sopenharmony_ci	} else {
241662306a36Sopenharmony_ci		/* If beyond the end of the current page -> an error. */
241762306a36Sopenharmony_ci		u32 page_off = lsn_to_page_off(log, lsn);
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci		if (page_off + len + log->record_header_len > log->page_size)
242062306a36Sopenharmony_ci			return -EINVAL;
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci		lcb->log_rec = Add2Ptr(rh, sizeof(struct LFS_RECORD_HDR));
242362306a36Sopenharmony_ci		lcb->alloc = false;
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	return 0;
242762306a36Sopenharmony_ci}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci/*
243062306a36Sopenharmony_ci * read_log_rec_lcb - Init the query operation.
243162306a36Sopenharmony_ci */
243262306a36Sopenharmony_cistatic int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode,
243362306a36Sopenharmony_ci			    struct lcb **lcb_)
243462306a36Sopenharmony_ci{
243562306a36Sopenharmony_ci	int err;
243662306a36Sopenharmony_ci	const struct CLIENT_REC *cr;
243762306a36Sopenharmony_ci	struct lcb *lcb;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	switch (ctx_mode) {
244062306a36Sopenharmony_ci	case lcb_ctx_undo_next:
244162306a36Sopenharmony_ci	case lcb_ctx_prev:
244262306a36Sopenharmony_ci	case lcb_ctx_next:
244362306a36Sopenharmony_ci		break;
244462306a36Sopenharmony_ci	default:
244562306a36Sopenharmony_ci		return -EINVAL;
244662306a36Sopenharmony_ci	}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	/* Check that the given lsn is the legal range for this client. */
244962306a36Sopenharmony_ci	cr = Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off));
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	if (!verify_client_lsn(log, cr, lsn))
245262306a36Sopenharmony_ci		return -EINVAL;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	lcb = kzalloc(sizeof(struct lcb), GFP_NOFS);
245562306a36Sopenharmony_ci	if (!lcb)
245662306a36Sopenharmony_ci		return -ENOMEM;
245762306a36Sopenharmony_ci	lcb->client = log->client_id;
245862306a36Sopenharmony_ci	lcb->ctx_mode = ctx_mode;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	/* Find the log record indicated by the given lsn. */
246162306a36Sopenharmony_ci	err = find_log_rec(log, lsn, lcb);
246262306a36Sopenharmony_ci	if (err)
246362306a36Sopenharmony_ci		goto out;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	*lcb_ = lcb;
246662306a36Sopenharmony_ci	return 0;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ciout:
246962306a36Sopenharmony_ci	lcb_put(lcb);
247062306a36Sopenharmony_ci	*lcb_ = NULL;
247162306a36Sopenharmony_ci	return err;
247262306a36Sopenharmony_ci}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci/*
247562306a36Sopenharmony_ci * find_client_next_lsn
247662306a36Sopenharmony_ci *
247762306a36Sopenharmony_ci * Attempt to find the next lsn to return to a client based on the context mode.
247862306a36Sopenharmony_ci */
247962306a36Sopenharmony_cistatic int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn)
248062306a36Sopenharmony_ci{
248162306a36Sopenharmony_ci	int err;
248262306a36Sopenharmony_ci	u64 next_lsn;
248362306a36Sopenharmony_ci	struct LFS_RECORD_HDR *hdr;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	hdr = lcb->lrh;
248662306a36Sopenharmony_ci	*lsn = 0;
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	if (lcb_ctx_next != lcb->ctx_mode)
248962306a36Sopenharmony_ci		goto check_undo_next;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	/* Loop as long as another lsn can be found. */
249262306a36Sopenharmony_ci	for (;;) {
249362306a36Sopenharmony_ci		u64 current_lsn;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci		err = next_log_lsn(log, hdr, &current_lsn);
249662306a36Sopenharmony_ci		if (err)
249762306a36Sopenharmony_ci			goto out;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci		if (!current_lsn)
250062306a36Sopenharmony_ci			break;
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci		if (hdr != lcb->lrh)
250362306a36Sopenharmony_ci			kfree(hdr);
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci		hdr = NULL;
250662306a36Sopenharmony_ci		err = read_log_page(log, lsn_to_vbo(log, current_lsn),
250762306a36Sopenharmony_ci				    (struct RECORD_PAGE_HDR **)&hdr, NULL);
250862306a36Sopenharmony_ci		if (err)
250962306a36Sopenharmony_ci			goto out;
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci		if (memcmp(&hdr->client, &lcb->client,
251262306a36Sopenharmony_ci			   sizeof(struct CLIENT_ID))) {
251362306a36Sopenharmony_ci			/*err = -EINVAL; */
251462306a36Sopenharmony_ci		} else if (LfsClientRecord == hdr->record_type) {
251562306a36Sopenharmony_ci			kfree(lcb->lrh);
251662306a36Sopenharmony_ci			lcb->lrh = hdr;
251762306a36Sopenharmony_ci			*lsn = current_lsn;
251862306a36Sopenharmony_ci			return 0;
251962306a36Sopenharmony_ci		}
252062306a36Sopenharmony_ci	}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ciout:
252362306a36Sopenharmony_ci	if (hdr != lcb->lrh)
252462306a36Sopenharmony_ci		kfree(hdr);
252562306a36Sopenharmony_ci	return err;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_cicheck_undo_next:
252862306a36Sopenharmony_ci	if (lcb_ctx_undo_next == lcb->ctx_mode)
252962306a36Sopenharmony_ci		next_lsn = le64_to_cpu(hdr->client_undo_next_lsn);
253062306a36Sopenharmony_ci	else if (lcb_ctx_prev == lcb->ctx_mode)
253162306a36Sopenharmony_ci		next_lsn = le64_to_cpu(hdr->client_prev_lsn);
253262306a36Sopenharmony_ci	else
253362306a36Sopenharmony_ci		return 0;
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	if (!next_lsn)
253662306a36Sopenharmony_ci		return 0;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	if (!verify_client_lsn(
253962306a36Sopenharmony_ci		    log, Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off)),
254062306a36Sopenharmony_ci		    next_lsn))
254162306a36Sopenharmony_ci		return 0;
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	hdr = NULL;
254462306a36Sopenharmony_ci	err = read_log_page(log, lsn_to_vbo(log, next_lsn),
254562306a36Sopenharmony_ci			    (struct RECORD_PAGE_HDR **)&hdr, NULL);
254662306a36Sopenharmony_ci	if (err)
254762306a36Sopenharmony_ci		return err;
254862306a36Sopenharmony_ci	kfree(lcb->lrh);
254962306a36Sopenharmony_ci	lcb->lrh = hdr;
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	*lsn = next_lsn;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	return 0;
255462306a36Sopenharmony_ci}
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_cistatic int read_next_log_rec(struct ntfs_log *log, struct lcb *lcb, u64 *lsn)
255762306a36Sopenharmony_ci{
255862306a36Sopenharmony_ci	int err;
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	err = find_client_next_lsn(log, lcb, lsn);
256162306a36Sopenharmony_ci	if (err)
256262306a36Sopenharmony_ci		return err;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	if (!*lsn)
256562306a36Sopenharmony_ci		return 0;
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	if (lcb->alloc)
256862306a36Sopenharmony_ci		kfree(lcb->log_rec);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	lcb->log_rec = NULL;
257162306a36Sopenharmony_ci	lcb->alloc = false;
257262306a36Sopenharmony_ci	kfree(lcb->lrh);
257362306a36Sopenharmony_ci	lcb->lrh = NULL;
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	return find_log_rec(log, *lsn, lcb);
257662306a36Sopenharmony_ci}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_cibool check_index_header(const struct INDEX_HDR *hdr, size_t bytes)
257962306a36Sopenharmony_ci{
258062306a36Sopenharmony_ci	__le16 mask;
258162306a36Sopenharmony_ci	u32 min_de, de_off, used, total;
258262306a36Sopenharmony_ci	const struct NTFS_DE *e;
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	if (hdr_has_subnode(hdr)) {
258562306a36Sopenharmony_ci		min_de = sizeof(struct NTFS_DE) + sizeof(u64);
258662306a36Sopenharmony_ci		mask = NTFS_IE_HAS_SUBNODES;
258762306a36Sopenharmony_ci	} else {
258862306a36Sopenharmony_ci		min_de = sizeof(struct NTFS_DE);
258962306a36Sopenharmony_ci		mask = 0;
259062306a36Sopenharmony_ci	}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	de_off = le32_to_cpu(hdr->de_off);
259362306a36Sopenharmony_ci	used = le32_to_cpu(hdr->used);
259462306a36Sopenharmony_ci	total = le32_to_cpu(hdr->total);
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	if (de_off > bytes - min_de || used > bytes || total > bytes ||
259762306a36Sopenharmony_ci	    de_off + min_de > used || used > total) {
259862306a36Sopenharmony_ci		return false;
259962306a36Sopenharmony_ci	}
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	e = Add2Ptr(hdr, de_off);
260262306a36Sopenharmony_ci	for (;;) {
260362306a36Sopenharmony_ci		u16 esize = le16_to_cpu(e->size);
260462306a36Sopenharmony_ci		struct NTFS_DE *next = Add2Ptr(e, esize);
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci		if (esize < min_de || PtrOffset(hdr, next) > used ||
260762306a36Sopenharmony_ci		    (e->flags & NTFS_IE_HAS_SUBNODES) != mask) {
260862306a36Sopenharmony_ci			return false;
260962306a36Sopenharmony_ci		}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci		if (de_is_last(e))
261262306a36Sopenharmony_ci			break;
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci		e = next;
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	return true;
261862306a36Sopenharmony_ci}
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_cistatic inline bool check_index_buffer(const struct INDEX_BUFFER *ib, u32 bytes)
262162306a36Sopenharmony_ci{
262262306a36Sopenharmony_ci	u16 fo;
262362306a36Sopenharmony_ci	const struct NTFS_RECORD_HEADER *r = &ib->rhdr;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	if (r->sign != NTFS_INDX_SIGNATURE)
262662306a36Sopenharmony_ci		return false;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	fo = (SECTOR_SIZE - ((bytes >> SECTOR_SHIFT) + 1) * sizeof(short));
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	if (le16_to_cpu(r->fix_off) > fo)
263162306a36Sopenharmony_ci		return false;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	if ((le16_to_cpu(r->fix_num) - 1) * SECTOR_SIZE != bytes)
263462306a36Sopenharmony_ci		return false;
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	return check_index_header(&ib->ihdr,
263762306a36Sopenharmony_ci				  bytes - offsetof(struct INDEX_BUFFER, ihdr));
263862306a36Sopenharmony_ci}
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_cistatic inline bool check_index_root(const struct ATTRIB *attr,
264162306a36Sopenharmony_ci				    struct ntfs_sb_info *sbi)
264262306a36Sopenharmony_ci{
264362306a36Sopenharmony_ci	bool ret;
264462306a36Sopenharmony_ci	const struct INDEX_ROOT *root = resident_data(attr);
264562306a36Sopenharmony_ci	u8 index_bits = le32_to_cpu(root->index_block_size) >=
264662306a36Sopenharmony_ci					sbi->cluster_size ?
264762306a36Sopenharmony_ci				sbi->cluster_bits :
264862306a36Sopenharmony_ci				SECTOR_SHIFT;
264962306a36Sopenharmony_ci	u8 block_clst = root->index_block_clst;
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	if (le32_to_cpu(attr->res.data_size) < sizeof(struct INDEX_ROOT) ||
265262306a36Sopenharmony_ci	    (root->type != ATTR_NAME && root->type != ATTR_ZERO) ||
265362306a36Sopenharmony_ci	    (root->type == ATTR_NAME &&
265462306a36Sopenharmony_ci	     root->rule != NTFS_COLLATION_TYPE_FILENAME) ||
265562306a36Sopenharmony_ci	    (le32_to_cpu(root->index_block_size) !=
265662306a36Sopenharmony_ci	     (block_clst << index_bits)) ||
265762306a36Sopenharmony_ci	    (block_clst != 1 && block_clst != 2 && block_clst != 4 &&
265862306a36Sopenharmony_ci	     block_clst != 8 && block_clst != 0x10 && block_clst != 0x20 &&
265962306a36Sopenharmony_ci	     block_clst != 0x40 && block_clst != 0x80)) {
266062306a36Sopenharmony_ci		return false;
266162306a36Sopenharmony_ci	}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	ret = check_index_header(&root->ihdr,
266462306a36Sopenharmony_ci				 le32_to_cpu(attr->res.data_size) -
266562306a36Sopenharmony_ci					 offsetof(struct INDEX_ROOT, ihdr));
266662306a36Sopenharmony_ci	return ret;
266762306a36Sopenharmony_ci}
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_cistatic inline bool check_attr(const struct MFT_REC *rec,
267062306a36Sopenharmony_ci			      const struct ATTRIB *attr,
267162306a36Sopenharmony_ci			      struct ntfs_sb_info *sbi)
267262306a36Sopenharmony_ci{
267362306a36Sopenharmony_ci	u32 asize = le32_to_cpu(attr->size);
267462306a36Sopenharmony_ci	u32 rsize = 0;
267562306a36Sopenharmony_ci	u64 dsize, svcn, evcn;
267662306a36Sopenharmony_ci	u16 run_off;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	/* Check the fixed part of the attribute record header. */
267962306a36Sopenharmony_ci	if (asize >= sbi->record_size ||
268062306a36Sopenharmony_ci	    asize + PtrOffset(rec, attr) >= sbi->record_size ||
268162306a36Sopenharmony_ci	    (attr->name_len &&
268262306a36Sopenharmony_ci	     le16_to_cpu(attr->name_off) + attr->name_len * sizeof(short) >
268362306a36Sopenharmony_ci		     asize)) {
268462306a36Sopenharmony_ci		return false;
268562306a36Sopenharmony_ci	}
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	/* Check the attribute fields. */
268862306a36Sopenharmony_ci	switch (attr->non_res) {
268962306a36Sopenharmony_ci	case 0:
269062306a36Sopenharmony_ci		rsize = le32_to_cpu(attr->res.data_size);
269162306a36Sopenharmony_ci		if (rsize >= asize ||
269262306a36Sopenharmony_ci		    le16_to_cpu(attr->res.data_off) + rsize > asize) {
269362306a36Sopenharmony_ci			return false;
269462306a36Sopenharmony_ci		}
269562306a36Sopenharmony_ci		break;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	case 1:
269862306a36Sopenharmony_ci		dsize = le64_to_cpu(attr->nres.data_size);
269962306a36Sopenharmony_ci		svcn = le64_to_cpu(attr->nres.svcn);
270062306a36Sopenharmony_ci		evcn = le64_to_cpu(attr->nres.evcn);
270162306a36Sopenharmony_ci		run_off = le16_to_cpu(attr->nres.run_off);
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci		if (svcn > evcn + 1 || run_off >= asize ||
270462306a36Sopenharmony_ci		    le64_to_cpu(attr->nres.valid_size) > dsize ||
270562306a36Sopenharmony_ci		    dsize > le64_to_cpu(attr->nres.alloc_size)) {
270662306a36Sopenharmony_ci			return false;
270762306a36Sopenharmony_ci		}
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci		if (run_off > asize)
271062306a36Sopenharmony_ci			return false;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci		if (run_unpack(NULL, sbi, 0, svcn, evcn, svcn,
271362306a36Sopenharmony_ci			       Add2Ptr(attr, run_off), asize - run_off) < 0) {
271462306a36Sopenharmony_ci			return false;
271562306a36Sopenharmony_ci		}
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci		return true;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	default:
272062306a36Sopenharmony_ci		return false;
272162306a36Sopenharmony_ci	}
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	switch (attr->type) {
272462306a36Sopenharmony_ci	case ATTR_NAME:
272562306a36Sopenharmony_ci		if (fname_full_size(Add2Ptr(
272662306a36Sopenharmony_ci			    attr, le16_to_cpu(attr->res.data_off))) > asize) {
272762306a36Sopenharmony_ci			return false;
272862306a36Sopenharmony_ci		}
272962306a36Sopenharmony_ci		break;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	case ATTR_ROOT:
273262306a36Sopenharmony_ci		return check_index_root(attr, sbi);
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	case ATTR_STD:
273562306a36Sopenharmony_ci		if (rsize < sizeof(struct ATTR_STD_INFO5) &&
273662306a36Sopenharmony_ci		    rsize != sizeof(struct ATTR_STD_INFO)) {
273762306a36Sopenharmony_ci			return false;
273862306a36Sopenharmony_ci		}
273962306a36Sopenharmony_ci		break;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	case ATTR_LIST:
274262306a36Sopenharmony_ci	case ATTR_ID:
274362306a36Sopenharmony_ci	case ATTR_SECURE:
274462306a36Sopenharmony_ci	case ATTR_LABEL:
274562306a36Sopenharmony_ci	case ATTR_VOL_INFO:
274662306a36Sopenharmony_ci	case ATTR_DATA:
274762306a36Sopenharmony_ci	case ATTR_ALLOC:
274862306a36Sopenharmony_ci	case ATTR_BITMAP:
274962306a36Sopenharmony_ci	case ATTR_REPARSE:
275062306a36Sopenharmony_ci	case ATTR_EA_INFO:
275162306a36Sopenharmony_ci	case ATTR_EA:
275262306a36Sopenharmony_ci	case ATTR_PROPERTYSET:
275362306a36Sopenharmony_ci	case ATTR_LOGGED_UTILITY_STREAM:
275462306a36Sopenharmony_ci		break;
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	default:
275762306a36Sopenharmony_ci		return false;
275862306a36Sopenharmony_ci	}
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	return true;
276162306a36Sopenharmony_ci}
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_cistatic inline bool check_file_record(const struct MFT_REC *rec,
276462306a36Sopenharmony_ci				     const struct MFT_REC *rec2,
276562306a36Sopenharmony_ci				     struct ntfs_sb_info *sbi)
276662306a36Sopenharmony_ci{
276762306a36Sopenharmony_ci	const struct ATTRIB *attr;
276862306a36Sopenharmony_ci	u16 fo = le16_to_cpu(rec->rhdr.fix_off);
276962306a36Sopenharmony_ci	u16 fn = le16_to_cpu(rec->rhdr.fix_num);
277062306a36Sopenharmony_ci	u16 ao = le16_to_cpu(rec->attr_off);
277162306a36Sopenharmony_ci	u32 rs = sbi->record_size;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	/* Check the file record header for consistency. */
277462306a36Sopenharmony_ci	if (rec->rhdr.sign != NTFS_FILE_SIGNATURE ||
277562306a36Sopenharmony_ci	    fo > (SECTOR_SIZE - ((rs >> SECTOR_SHIFT) + 1) * sizeof(short)) ||
277662306a36Sopenharmony_ci	    (fn - 1) * SECTOR_SIZE != rs || ao < MFTRECORD_FIXUP_OFFSET_1 ||
277762306a36Sopenharmony_ci	    ao > sbi->record_size - SIZEOF_RESIDENT || !is_rec_inuse(rec) ||
277862306a36Sopenharmony_ci	    le32_to_cpu(rec->total) != rs) {
277962306a36Sopenharmony_ci		return false;
278062306a36Sopenharmony_ci	}
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	/* Loop to check all of the attributes. */
278362306a36Sopenharmony_ci	for (attr = Add2Ptr(rec, ao); attr->type != ATTR_END;
278462306a36Sopenharmony_ci	     attr = Add2Ptr(attr, le32_to_cpu(attr->size))) {
278562306a36Sopenharmony_ci		if (check_attr(rec, attr, sbi))
278662306a36Sopenharmony_ci			continue;
278762306a36Sopenharmony_ci		return false;
278862306a36Sopenharmony_ci	}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	return true;
279162306a36Sopenharmony_ci}
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_cistatic inline int check_lsn(const struct NTFS_RECORD_HEADER *hdr,
279462306a36Sopenharmony_ci			    const u64 *rlsn)
279562306a36Sopenharmony_ci{
279662306a36Sopenharmony_ci	u64 lsn;
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	if (!rlsn)
279962306a36Sopenharmony_ci		return true;
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	lsn = le64_to_cpu(hdr->lsn);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	if (hdr->sign == NTFS_HOLE_SIGNATURE)
280462306a36Sopenharmony_ci		return false;
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	if (*rlsn > lsn)
280762306a36Sopenharmony_ci		return true;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	return false;
281062306a36Sopenharmony_ci}
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_cistatic inline bool check_if_attr(const struct MFT_REC *rec,
281362306a36Sopenharmony_ci				 const struct LOG_REC_HDR *lrh)
281462306a36Sopenharmony_ci{
281562306a36Sopenharmony_ci	u16 ro = le16_to_cpu(lrh->record_off);
281662306a36Sopenharmony_ci	u16 o = le16_to_cpu(rec->attr_off);
281762306a36Sopenharmony_ci	const struct ATTRIB *attr = Add2Ptr(rec, o);
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	while (o < ro) {
282062306a36Sopenharmony_ci		u32 asize;
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci		if (attr->type == ATTR_END)
282362306a36Sopenharmony_ci			break;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci		asize = le32_to_cpu(attr->size);
282662306a36Sopenharmony_ci		if (!asize)
282762306a36Sopenharmony_ci			break;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci		o += asize;
283062306a36Sopenharmony_ci		attr = Add2Ptr(attr, asize);
283162306a36Sopenharmony_ci	}
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	return o == ro;
283462306a36Sopenharmony_ci}
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_cistatic inline bool check_if_index_root(const struct MFT_REC *rec,
283762306a36Sopenharmony_ci				       const struct LOG_REC_HDR *lrh)
283862306a36Sopenharmony_ci{
283962306a36Sopenharmony_ci	u16 ro = le16_to_cpu(lrh->record_off);
284062306a36Sopenharmony_ci	u16 o = le16_to_cpu(rec->attr_off);
284162306a36Sopenharmony_ci	const struct ATTRIB *attr = Add2Ptr(rec, o);
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	while (o < ro) {
284462306a36Sopenharmony_ci		u32 asize;
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci		if (attr->type == ATTR_END)
284762306a36Sopenharmony_ci			break;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci		asize = le32_to_cpu(attr->size);
285062306a36Sopenharmony_ci		if (!asize)
285162306a36Sopenharmony_ci			break;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci		o += asize;
285462306a36Sopenharmony_ci		attr = Add2Ptr(attr, asize);
285562306a36Sopenharmony_ci	}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci	return o == ro && attr->type == ATTR_ROOT;
285862306a36Sopenharmony_ci}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_cistatic inline bool check_if_root_index(const struct ATTRIB *attr,
286162306a36Sopenharmony_ci				       const struct INDEX_HDR *hdr,
286262306a36Sopenharmony_ci				       const struct LOG_REC_HDR *lrh)
286362306a36Sopenharmony_ci{
286462306a36Sopenharmony_ci	u16 ao = le16_to_cpu(lrh->attr_off);
286562306a36Sopenharmony_ci	u32 de_off = le32_to_cpu(hdr->de_off);
286662306a36Sopenharmony_ci	u32 o = PtrOffset(attr, hdr) + de_off;
286762306a36Sopenharmony_ci	const struct NTFS_DE *e = Add2Ptr(hdr, de_off);
286862306a36Sopenharmony_ci	u32 asize = le32_to_cpu(attr->size);
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	while (o < ao) {
287162306a36Sopenharmony_ci		u16 esize;
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci		if (o >= asize)
287462306a36Sopenharmony_ci			break;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci		esize = le16_to_cpu(e->size);
287762306a36Sopenharmony_ci		if (!esize)
287862306a36Sopenharmony_ci			break;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci		o += esize;
288162306a36Sopenharmony_ci		e = Add2Ptr(e, esize);
288262306a36Sopenharmony_ci	}
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci	return o == ao;
288562306a36Sopenharmony_ci}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_cistatic inline bool check_if_alloc_index(const struct INDEX_HDR *hdr,
288862306a36Sopenharmony_ci					u32 attr_off)
288962306a36Sopenharmony_ci{
289062306a36Sopenharmony_ci	u32 de_off = le32_to_cpu(hdr->de_off);
289162306a36Sopenharmony_ci	u32 o = offsetof(struct INDEX_BUFFER, ihdr) + de_off;
289262306a36Sopenharmony_ci	const struct NTFS_DE *e = Add2Ptr(hdr, de_off);
289362306a36Sopenharmony_ci	u32 used = le32_to_cpu(hdr->used);
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci	while (o < attr_off) {
289662306a36Sopenharmony_ci		u16 esize;
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci		if (de_off >= used)
289962306a36Sopenharmony_ci			break;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci		esize = le16_to_cpu(e->size);
290262306a36Sopenharmony_ci		if (!esize)
290362306a36Sopenharmony_ci			break;
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci		o += esize;
290662306a36Sopenharmony_ci		de_off += esize;
290762306a36Sopenharmony_ci		e = Add2Ptr(e, esize);
290862306a36Sopenharmony_ci	}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	return o == attr_off;
291162306a36Sopenharmony_ci}
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_cistatic inline void change_attr_size(struct MFT_REC *rec, struct ATTRIB *attr,
291462306a36Sopenharmony_ci				    u32 nsize)
291562306a36Sopenharmony_ci{
291662306a36Sopenharmony_ci	u32 asize = le32_to_cpu(attr->size);
291762306a36Sopenharmony_ci	int dsize = nsize - asize;
291862306a36Sopenharmony_ci	u8 *next = Add2Ptr(attr, asize);
291962306a36Sopenharmony_ci	u32 used = le32_to_cpu(rec->used);
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	memmove(Add2Ptr(attr, nsize), next, used - PtrOffset(rec, next));
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci	rec->used = cpu_to_le32(used + dsize);
292462306a36Sopenharmony_ci	attr->size = cpu_to_le32(nsize);
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_cistruct OpenAttr {
292862306a36Sopenharmony_ci	struct ATTRIB *attr;
292962306a36Sopenharmony_ci	struct runs_tree *run1;
293062306a36Sopenharmony_ci	struct runs_tree run0;
293162306a36Sopenharmony_ci	struct ntfs_inode *ni;
293262306a36Sopenharmony_ci	// CLST rno;
293362306a36Sopenharmony_ci};
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci/*
293662306a36Sopenharmony_ci * cmp_type_and_name
293762306a36Sopenharmony_ci *
293862306a36Sopenharmony_ci * Return: 0 if 'attr' has the same type and name.
293962306a36Sopenharmony_ci */
294062306a36Sopenharmony_cistatic inline int cmp_type_and_name(const struct ATTRIB *a1,
294162306a36Sopenharmony_ci				    const struct ATTRIB *a2)
294262306a36Sopenharmony_ci{
294362306a36Sopenharmony_ci	return a1->type != a2->type || a1->name_len != a2->name_len ||
294462306a36Sopenharmony_ci	       (a1->name_len && memcmp(attr_name(a1), attr_name(a2),
294562306a36Sopenharmony_ci				       a1->name_len * sizeof(short)));
294662306a36Sopenharmony_ci}
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_cistatic struct OpenAttr *find_loaded_attr(struct ntfs_log *log,
294962306a36Sopenharmony_ci					 const struct ATTRIB *attr, CLST rno)
295062306a36Sopenharmony_ci{
295162306a36Sopenharmony_ci	struct OPEN_ATTR_ENRTY *oe = NULL;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	while ((oe = enum_rstbl(log->open_attr_tbl, oe))) {
295462306a36Sopenharmony_ci		struct OpenAttr *op_attr;
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci		if (ino_get(&oe->ref) != rno)
295762306a36Sopenharmony_ci			continue;
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci		op_attr = (struct OpenAttr *)oe->ptr;
296062306a36Sopenharmony_ci		if (!cmp_type_and_name(op_attr->attr, attr))
296162306a36Sopenharmony_ci			return op_attr;
296262306a36Sopenharmony_ci	}
296362306a36Sopenharmony_ci	return NULL;
296462306a36Sopenharmony_ci}
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_cistatic struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi,
296762306a36Sopenharmony_ci					     enum ATTR_TYPE type, u64 size,
296862306a36Sopenharmony_ci					     const u16 *name, size_t name_len,
296962306a36Sopenharmony_ci					     __le16 flags)
297062306a36Sopenharmony_ci{
297162306a36Sopenharmony_ci	struct ATTRIB *attr;
297262306a36Sopenharmony_ci	u32 name_size = ALIGN(name_len * sizeof(short), 8);
297362306a36Sopenharmony_ci	bool is_ext = flags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED);
297462306a36Sopenharmony_ci	u32 asize = name_size +
297562306a36Sopenharmony_ci		    (is_ext ? SIZEOF_NONRESIDENT_EX : SIZEOF_NONRESIDENT);
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	attr = kzalloc(asize, GFP_NOFS);
297862306a36Sopenharmony_ci	if (!attr)
297962306a36Sopenharmony_ci		return NULL;
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	attr->type = type;
298262306a36Sopenharmony_ci	attr->size = cpu_to_le32(asize);
298362306a36Sopenharmony_ci	attr->flags = flags;
298462306a36Sopenharmony_ci	attr->non_res = 1;
298562306a36Sopenharmony_ci	attr->name_len = name_len;
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci	attr->nres.evcn = cpu_to_le64((u64)bytes_to_cluster(sbi, size) - 1);
298862306a36Sopenharmony_ci	attr->nres.alloc_size = cpu_to_le64(ntfs_up_cluster(sbi, size));
298962306a36Sopenharmony_ci	attr->nres.data_size = cpu_to_le64(size);
299062306a36Sopenharmony_ci	attr->nres.valid_size = attr->nres.data_size;
299162306a36Sopenharmony_ci	if (is_ext) {
299262306a36Sopenharmony_ci		attr->name_off = SIZEOF_NONRESIDENT_EX_LE;
299362306a36Sopenharmony_ci		if (is_attr_compressed(attr))
299462306a36Sopenharmony_ci			attr->nres.c_unit = COMPRESSION_UNIT;
299562306a36Sopenharmony_ci
299662306a36Sopenharmony_ci		attr->nres.run_off =
299762306a36Sopenharmony_ci			cpu_to_le16(SIZEOF_NONRESIDENT_EX + name_size);
299862306a36Sopenharmony_ci		memcpy(Add2Ptr(attr, SIZEOF_NONRESIDENT_EX), name,
299962306a36Sopenharmony_ci		       name_len * sizeof(short));
300062306a36Sopenharmony_ci	} else {
300162306a36Sopenharmony_ci		attr->name_off = SIZEOF_NONRESIDENT_LE;
300262306a36Sopenharmony_ci		attr->nres.run_off =
300362306a36Sopenharmony_ci			cpu_to_le16(SIZEOF_NONRESIDENT + name_size);
300462306a36Sopenharmony_ci		memcpy(Add2Ptr(attr, SIZEOF_NONRESIDENT), name,
300562306a36Sopenharmony_ci		       name_len * sizeof(short));
300662306a36Sopenharmony_ci	}
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	return attr;
300962306a36Sopenharmony_ci}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci/*
301262306a36Sopenharmony_ci * do_action - Common routine for the Redo and Undo Passes.
301362306a36Sopenharmony_ci * @rlsn: If it is NULL then undo.
301462306a36Sopenharmony_ci */
301562306a36Sopenharmony_cistatic int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
301662306a36Sopenharmony_ci		     const struct LOG_REC_HDR *lrh, u32 op, void *data,
301762306a36Sopenharmony_ci		     u32 dlen, u32 rec_len, const u64 *rlsn)
301862306a36Sopenharmony_ci{
301962306a36Sopenharmony_ci	int err = 0;
302062306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = log->ni->mi.sbi;
302162306a36Sopenharmony_ci	struct inode *inode = NULL, *inode_parent;
302262306a36Sopenharmony_ci	struct mft_inode *mi = NULL, *mi2_child = NULL;
302362306a36Sopenharmony_ci	CLST rno = 0, rno_base = 0;
302462306a36Sopenharmony_ci	struct INDEX_BUFFER *ib = NULL;
302562306a36Sopenharmony_ci	struct MFT_REC *rec = NULL;
302662306a36Sopenharmony_ci	struct ATTRIB *attr = NULL, *attr2;
302762306a36Sopenharmony_ci	struct INDEX_HDR *hdr;
302862306a36Sopenharmony_ci	struct INDEX_ROOT *root;
302962306a36Sopenharmony_ci	struct NTFS_DE *e, *e1, *e2;
303062306a36Sopenharmony_ci	struct NEW_ATTRIBUTE_SIZES *new_sz;
303162306a36Sopenharmony_ci	struct ATTR_FILE_NAME *fname;
303262306a36Sopenharmony_ci	struct OpenAttr *oa, *oa2;
303362306a36Sopenharmony_ci	u32 nsize, t32, asize, used, esize, off, bits;
303462306a36Sopenharmony_ci	u16 id, id2;
303562306a36Sopenharmony_ci	u32 record_size = sbi->record_size;
303662306a36Sopenharmony_ci	u64 t64;
303762306a36Sopenharmony_ci	u16 roff = le16_to_cpu(lrh->record_off);
303862306a36Sopenharmony_ci	u16 aoff = le16_to_cpu(lrh->attr_off);
303962306a36Sopenharmony_ci	u64 lco = 0;
304062306a36Sopenharmony_ci	u64 cbo = (u64)le16_to_cpu(lrh->cluster_off) << SECTOR_SHIFT;
304162306a36Sopenharmony_ci	u64 tvo = le64_to_cpu(lrh->target_vcn) << sbi->cluster_bits;
304262306a36Sopenharmony_ci	u64 vbo = cbo + tvo;
304362306a36Sopenharmony_ci	void *buffer_le = NULL;
304462306a36Sopenharmony_ci	u32 bytes = 0;
304562306a36Sopenharmony_ci	bool a_dirty = false;
304662306a36Sopenharmony_ci	u16 data_off;
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci	oa = oe->ptr;
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	/* Big switch to prepare. */
305162306a36Sopenharmony_ci	switch (op) {
305262306a36Sopenharmony_ci	/* ============================================================
305362306a36Sopenharmony_ci	 * Process MFT records, as described by the current log record.
305462306a36Sopenharmony_ci	 * ============================================================
305562306a36Sopenharmony_ci	 */
305662306a36Sopenharmony_ci	case InitializeFileRecordSegment:
305762306a36Sopenharmony_ci	case DeallocateFileRecordSegment:
305862306a36Sopenharmony_ci	case WriteEndOfFileRecordSegment:
305962306a36Sopenharmony_ci	case CreateAttribute:
306062306a36Sopenharmony_ci	case DeleteAttribute:
306162306a36Sopenharmony_ci	case UpdateResidentValue:
306262306a36Sopenharmony_ci	case UpdateMappingPairs:
306362306a36Sopenharmony_ci	case SetNewAttributeSizes:
306462306a36Sopenharmony_ci	case AddIndexEntryRoot:
306562306a36Sopenharmony_ci	case DeleteIndexEntryRoot:
306662306a36Sopenharmony_ci	case SetIndexEntryVcnRoot:
306762306a36Sopenharmony_ci	case UpdateFileNameRoot:
306862306a36Sopenharmony_ci	case UpdateRecordDataRoot:
306962306a36Sopenharmony_ci	case ZeroEndOfFileRecord:
307062306a36Sopenharmony_ci		rno = vbo >> sbi->record_bits;
307162306a36Sopenharmony_ci		inode = ilookup(sbi->sb, rno);
307262306a36Sopenharmony_ci		if (inode) {
307362306a36Sopenharmony_ci			mi = &ntfs_i(inode)->mi;
307462306a36Sopenharmony_ci		} else if (op == InitializeFileRecordSegment) {
307562306a36Sopenharmony_ci			mi = kzalloc(sizeof(struct mft_inode), GFP_NOFS);
307662306a36Sopenharmony_ci			if (!mi)
307762306a36Sopenharmony_ci				return -ENOMEM;
307862306a36Sopenharmony_ci			err = mi_format_new(mi, sbi, rno, 0, false);
307962306a36Sopenharmony_ci			if (err)
308062306a36Sopenharmony_ci				goto out;
308162306a36Sopenharmony_ci		} else {
308262306a36Sopenharmony_ci			/* Read from disk. */
308362306a36Sopenharmony_ci			err = mi_get(sbi, rno, &mi);
308462306a36Sopenharmony_ci			if (err)
308562306a36Sopenharmony_ci				return err;
308662306a36Sopenharmony_ci		}
308762306a36Sopenharmony_ci		rec = mi->mrec;
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci		if (op == DeallocateFileRecordSegment)
309062306a36Sopenharmony_ci			goto skip_load_parent;
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci		if (InitializeFileRecordSegment != op) {
309362306a36Sopenharmony_ci			if (rec->rhdr.sign == NTFS_BAAD_SIGNATURE)
309462306a36Sopenharmony_ci				goto dirty_vol;
309562306a36Sopenharmony_ci			if (!check_lsn(&rec->rhdr, rlsn))
309662306a36Sopenharmony_ci				goto out;
309762306a36Sopenharmony_ci			if (!check_file_record(rec, NULL, sbi))
309862306a36Sopenharmony_ci				goto dirty_vol;
309962306a36Sopenharmony_ci			attr = Add2Ptr(rec, roff);
310062306a36Sopenharmony_ci		}
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci		if (is_rec_base(rec) || InitializeFileRecordSegment == op) {
310362306a36Sopenharmony_ci			rno_base = rno;
310462306a36Sopenharmony_ci			goto skip_load_parent;
310562306a36Sopenharmony_ci		}
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci		rno_base = ino_get(&rec->parent_ref);
310862306a36Sopenharmony_ci		inode_parent = ntfs_iget5(sbi->sb, &rec->parent_ref, NULL);
310962306a36Sopenharmony_ci		if (IS_ERR(inode_parent))
311062306a36Sopenharmony_ci			goto skip_load_parent;
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci		if (is_bad_inode(inode_parent)) {
311362306a36Sopenharmony_ci			iput(inode_parent);
311462306a36Sopenharmony_ci			goto skip_load_parent;
311562306a36Sopenharmony_ci		}
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci		if (ni_load_mi_ex(ntfs_i(inode_parent), rno, &mi2_child)) {
311862306a36Sopenharmony_ci			iput(inode_parent);
311962306a36Sopenharmony_ci		} else {
312062306a36Sopenharmony_ci			if (mi2_child->mrec != mi->mrec)
312162306a36Sopenharmony_ci				memcpy(mi2_child->mrec, mi->mrec,
312262306a36Sopenharmony_ci				       sbi->record_size);
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci			if (inode)
312562306a36Sopenharmony_ci				iput(inode);
312662306a36Sopenharmony_ci			else if (mi)
312762306a36Sopenharmony_ci				mi_put(mi);
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci			inode = inode_parent;
313062306a36Sopenharmony_ci			mi = mi2_child;
313162306a36Sopenharmony_ci			rec = mi2_child->mrec;
313262306a36Sopenharmony_ci			attr = Add2Ptr(rec, roff);
313362306a36Sopenharmony_ci		}
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ciskip_load_parent:
313662306a36Sopenharmony_ci		inode_parent = NULL;
313762306a36Sopenharmony_ci		break;
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	/*
314062306a36Sopenharmony_ci	 * Process attributes, as described by the current log record.
314162306a36Sopenharmony_ci	 */
314262306a36Sopenharmony_ci	case UpdateNonresidentValue:
314362306a36Sopenharmony_ci	case AddIndexEntryAllocation:
314462306a36Sopenharmony_ci	case DeleteIndexEntryAllocation:
314562306a36Sopenharmony_ci	case WriteEndOfIndexBuffer:
314662306a36Sopenharmony_ci	case SetIndexEntryVcnAllocation:
314762306a36Sopenharmony_ci	case UpdateFileNameAllocation:
314862306a36Sopenharmony_ci	case SetBitsInNonresidentBitMap:
314962306a36Sopenharmony_ci	case ClearBitsInNonresidentBitMap:
315062306a36Sopenharmony_ci	case UpdateRecordDataAllocation:
315162306a36Sopenharmony_ci		attr = oa->attr;
315262306a36Sopenharmony_ci		bytes = UpdateNonresidentValue == op ? dlen : 0;
315362306a36Sopenharmony_ci		lco = (u64)le16_to_cpu(lrh->lcns_follow) << sbi->cluster_bits;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci		if (attr->type == ATTR_ALLOC) {
315662306a36Sopenharmony_ci			t32 = le32_to_cpu(oe->bytes_per_index);
315762306a36Sopenharmony_ci			if (bytes < t32)
315862306a36Sopenharmony_ci				bytes = t32;
315962306a36Sopenharmony_ci		}
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci		if (!bytes)
316262306a36Sopenharmony_ci			bytes = lco - cbo;
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci		bytes += roff;
316562306a36Sopenharmony_ci		if (attr->type == ATTR_ALLOC)
316662306a36Sopenharmony_ci			bytes = (bytes + 511) & ~511; // align
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci		buffer_le = kmalloc(bytes, GFP_NOFS);
316962306a36Sopenharmony_ci		if (!buffer_le)
317062306a36Sopenharmony_ci			return -ENOMEM;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci		err = ntfs_read_run_nb(sbi, oa->run1, vbo, buffer_le, bytes,
317362306a36Sopenharmony_ci				       NULL);
317462306a36Sopenharmony_ci		if (err)
317562306a36Sopenharmony_ci			goto out;
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci		if (attr->type == ATTR_ALLOC && *(int *)buffer_le)
317862306a36Sopenharmony_ci			ntfs_fix_post_read(buffer_le, bytes, false);
317962306a36Sopenharmony_ci		break;
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci	default:
318262306a36Sopenharmony_ci		WARN_ON(1);
318362306a36Sopenharmony_ci	}
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	/* Big switch to do operation. */
318662306a36Sopenharmony_ci	switch (op) {
318762306a36Sopenharmony_ci	case InitializeFileRecordSegment:
318862306a36Sopenharmony_ci		if (roff + dlen > record_size)
318962306a36Sopenharmony_ci			goto dirty_vol;
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci		memcpy(Add2Ptr(rec, roff), data, dlen);
319262306a36Sopenharmony_ci		mi->dirty = true;
319362306a36Sopenharmony_ci		break;
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	case DeallocateFileRecordSegment:
319662306a36Sopenharmony_ci		clear_rec_inuse(rec);
319762306a36Sopenharmony_ci		le16_add_cpu(&rec->seq, 1);
319862306a36Sopenharmony_ci		mi->dirty = true;
319962306a36Sopenharmony_ci		break;
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci	case WriteEndOfFileRecordSegment:
320262306a36Sopenharmony_ci		attr2 = (struct ATTRIB *)data;
320362306a36Sopenharmony_ci		if (!check_if_attr(rec, lrh) || roff + dlen > record_size)
320462306a36Sopenharmony_ci			goto dirty_vol;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci		memmove(attr, attr2, dlen);
320762306a36Sopenharmony_ci		rec->used = cpu_to_le32(ALIGN(roff + dlen, 8));
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci		mi->dirty = true;
321062306a36Sopenharmony_ci		break;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	case CreateAttribute:
321362306a36Sopenharmony_ci		attr2 = (struct ATTRIB *)data;
321462306a36Sopenharmony_ci		asize = le32_to_cpu(attr2->size);
321562306a36Sopenharmony_ci		used = le32_to_cpu(rec->used);
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci		if (!check_if_attr(rec, lrh) || dlen < SIZEOF_RESIDENT ||
321862306a36Sopenharmony_ci		    !IS_ALIGNED(asize, 8) ||
321962306a36Sopenharmony_ci		    Add2Ptr(attr2, asize) > Add2Ptr(lrh, rec_len) ||
322062306a36Sopenharmony_ci		    dlen > record_size - used) {
322162306a36Sopenharmony_ci			goto dirty_vol;
322262306a36Sopenharmony_ci		}
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci		memmove(Add2Ptr(attr, asize), attr, used - roff);
322562306a36Sopenharmony_ci		memcpy(attr, attr2, asize);
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci		rec->used = cpu_to_le32(used + asize);
322862306a36Sopenharmony_ci		id = le16_to_cpu(rec->next_attr_id);
322962306a36Sopenharmony_ci		id2 = le16_to_cpu(attr2->id);
323062306a36Sopenharmony_ci		if (id <= id2)
323162306a36Sopenharmony_ci			rec->next_attr_id = cpu_to_le16(id2 + 1);
323262306a36Sopenharmony_ci		if (is_attr_indexed(attr))
323362306a36Sopenharmony_ci			le16_add_cpu(&rec->hard_links, 1);
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci		oa2 = find_loaded_attr(log, attr, rno_base);
323662306a36Sopenharmony_ci		if (oa2) {
323762306a36Sopenharmony_ci			void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
323862306a36Sopenharmony_ci					   GFP_NOFS);
323962306a36Sopenharmony_ci			if (p2) {
324062306a36Sopenharmony_ci				// run_close(oa2->run1);
324162306a36Sopenharmony_ci				kfree(oa2->attr);
324262306a36Sopenharmony_ci				oa2->attr = p2;
324362306a36Sopenharmony_ci			}
324462306a36Sopenharmony_ci		}
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci		mi->dirty = true;
324762306a36Sopenharmony_ci		break;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci	case DeleteAttribute:
325062306a36Sopenharmony_ci		asize = le32_to_cpu(attr->size);
325162306a36Sopenharmony_ci		used = le32_to_cpu(rec->used);
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci		if (!check_if_attr(rec, lrh))
325462306a36Sopenharmony_ci			goto dirty_vol;
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci		rec->used = cpu_to_le32(used - asize);
325762306a36Sopenharmony_ci		if (is_attr_indexed(attr))
325862306a36Sopenharmony_ci			le16_add_cpu(&rec->hard_links, -1);
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci		memmove(attr, Add2Ptr(attr, asize), used - asize - roff);
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci		mi->dirty = true;
326362306a36Sopenharmony_ci		break;
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci	case UpdateResidentValue:
326662306a36Sopenharmony_ci		nsize = aoff + dlen;
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci		if (!check_if_attr(rec, lrh))
326962306a36Sopenharmony_ci			goto dirty_vol;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci		asize = le32_to_cpu(attr->size);
327262306a36Sopenharmony_ci		used = le32_to_cpu(rec->used);
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci		if (lrh->redo_len == lrh->undo_len) {
327562306a36Sopenharmony_ci			if (nsize > asize)
327662306a36Sopenharmony_ci				goto dirty_vol;
327762306a36Sopenharmony_ci			goto move_data;
327862306a36Sopenharmony_ci		}
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci		if (nsize > asize && nsize - asize > record_size - used)
328162306a36Sopenharmony_ci			goto dirty_vol;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci		nsize = ALIGN(nsize, 8);
328462306a36Sopenharmony_ci		data_off = le16_to_cpu(attr->res.data_off);
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci		if (nsize < asize) {
328762306a36Sopenharmony_ci			memmove(Add2Ptr(attr, aoff), data, dlen);
328862306a36Sopenharmony_ci			data = NULL; // To skip below memmove().
328962306a36Sopenharmony_ci		}
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci		memmove(Add2Ptr(attr, nsize), Add2Ptr(attr, asize),
329262306a36Sopenharmony_ci			used - le16_to_cpu(lrh->record_off) - asize);
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci		rec->used = cpu_to_le32(used + nsize - asize);
329562306a36Sopenharmony_ci		attr->size = cpu_to_le32(nsize);
329662306a36Sopenharmony_ci		attr->res.data_size = cpu_to_le32(aoff + dlen - data_off);
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_cimove_data:
329962306a36Sopenharmony_ci		if (data)
330062306a36Sopenharmony_ci			memmove(Add2Ptr(attr, aoff), data, dlen);
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci		oa2 = find_loaded_attr(log, attr, rno_base);
330362306a36Sopenharmony_ci		if (oa2) {
330462306a36Sopenharmony_ci			void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
330562306a36Sopenharmony_ci					   GFP_NOFS);
330662306a36Sopenharmony_ci			if (p2) {
330762306a36Sopenharmony_ci				// run_close(&oa2->run0);
330862306a36Sopenharmony_ci				oa2->run1 = &oa2->run0;
330962306a36Sopenharmony_ci				kfree(oa2->attr);
331062306a36Sopenharmony_ci				oa2->attr = p2;
331162306a36Sopenharmony_ci			}
331262306a36Sopenharmony_ci		}
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci		mi->dirty = true;
331562306a36Sopenharmony_ci		break;
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci	case UpdateMappingPairs:
331862306a36Sopenharmony_ci		nsize = aoff + dlen;
331962306a36Sopenharmony_ci		asize = le32_to_cpu(attr->size);
332062306a36Sopenharmony_ci		used = le32_to_cpu(rec->used);
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci		if (!check_if_attr(rec, lrh) || !attr->non_res ||
332362306a36Sopenharmony_ci		    aoff < le16_to_cpu(attr->nres.run_off) || aoff > asize ||
332462306a36Sopenharmony_ci		    (nsize > asize && nsize - asize > record_size - used)) {
332562306a36Sopenharmony_ci			goto dirty_vol;
332662306a36Sopenharmony_ci		}
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci		nsize = ALIGN(nsize, 8);
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci		memmove(Add2Ptr(attr, nsize), Add2Ptr(attr, asize),
333162306a36Sopenharmony_ci			used - le16_to_cpu(lrh->record_off) - asize);
333262306a36Sopenharmony_ci		rec->used = cpu_to_le32(used + nsize - asize);
333362306a36Sopenharmony_ci		attr->size = cpu_to_le32(nsize);
333462306a36Sopenharmony_ci		memmove(Add2Ptr(attr, aoff), data, dlen);
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci		if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn),
333762306a36Sopenharmony_ci					attr_run(attr), &t64)) {
333862306a36Sopenharmony_ci			goto dirty_vol;
333962306a36Sopenharmony_ci		}
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci		attr->nres.evcn = cpu_to_le64(t64);
334262306a36Sopenharmony_ci		oa2 = find_loaded_attr(log, attr, rno_base);
334362306a36Sopenharmony_ci		if (oa2 && oa2->attr->non_res)
334462306a36Sopenharmony_ci			oa2->attr->nres.evcn = attr->nres.evcn;
334562306a36Sopenharmony_ci
334662306a36Sopenharmony_ci		mi->dirty = true;
334762306a36Sopenharmony_ci		break;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	case SetNewAttributeSizes:
335062306a36Sopenharmony_ci		new_sz = data;
335162306a36Sopenharmony_ci		if (!check_if_attr(rec, lrh) || !attr->non_res)
335262306a36Sopenharmony_ci			goto dirty_vol;
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci		attr->nres.alloc_size = new_sz->alloc_size;
335562306a36Sopenharmony_ci		attr->nres.data_size = new_sz->data_size;
335662306a36Sopenharmony_ci		attr->nres.valid_size = new_sz->valid_size;
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_ci		if (dlen >= sizeof(struct NEW_ATTRIBUTE_SIZES))
335962306a36Sopenharmony_ci			attr->nres.total_size = new_sz->total_size;
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci		oa2 = find_loaded_attr(log, attr, rno_base);
336262306a36Sopenharmony_ci		if (oa2) {
336362306a36Sopenharmony_ci			void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
336462306a36Sopenharmony_ci					   GFP_NOFS);
336562306a36Sopenharmony_ci			if (p2) {
336662306a36Sopenharmony_ci				kfree(oa2->attr);
336762306a36Sopenharmony_ci				oa2->attr = p2;
336862306a36Sopenharmony_ci			}
336962306a36Sopenharmony_ci		}
337062306a36Sopenharmony_ci		mi->dirty = true;
337162306a36Sopenharmony_ci		break;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	case AddIndexEntryRoot:
337462306a36Sopenharmony_ci		e = (struct NTFS_DE *)data;
337562306a36Sopenharmony_ci		esize = le16_to_cpu(e->size);
337662306a36Sopenharmony_ci		root = resident_data(attr);
337762306a36Sopenharmony_ci		hdr = &root->ihdr;
337862306a36Sopenharmony_ci		used = le32_to_cpu(hdr->used);
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci		if (!check_if_index_root(rec, lrh) ||
338162306a36Sopenharmony_ci		    !check_if_root_index(attr, hdr, lrh) ||
338262306a36Sopenharmony_ci		    Add2Ptr(data, esize) > Add2Ptr(lrh, rec_len) ||
338362306a36Sopenharmony_ci		    esize > le32_to_cpu(rec->total) - le32_to_cpu(rec->used)) {
338462306a36Sopenharmony_ci			goto dirty_vol;
338562306a36Sopenharmony_ci		}
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci		e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci		change_attr_size(rec, attr, le32_to_cpu(attr->size) + esize);
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci		memmove(Add2Ptr(e1, esize), e1,
339262306a36Sopenharmony_ci			PtrOffset(e1, Add2Ptr(hdr, used)));
339362306a36Sopenharmony_ci		memmove(e1, e, esize);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci		le32_add_cpu(&attr->res.data_size, esize);
339662306a36Sopenharmony_ci		hdr->used = cpu_to_le32(used + esize);
339762306a36Sopenharmony_ci		le32_add_cpu(&hdr->total, esize);
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci		mi->dirty = true;
340062306a36Sopenharmony_ci		break;
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	case DeleteIndexEntryRoot:
340362306a36Sopenharmony_ci		root = resident_data(attr);
340462306a36Sopenharmony_ci		hdr = &root->ihdr;
340562306a36Sopenharmony_ci		used = le32_to_cpu(hdr->used);
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci		if (!check_if_index_root(rec, lrh) ||
340862306a36Sopenharmony_ci		    !check_if_root_index(attr, hdr, lrh)) {
340962306a36Sopenharmony_ci			goto dirty_vol;
341062306a36Sopenharmony_ci		}
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci		e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
341362306a36Sopenharmony_ci		esize = le16_to_cpu(e1->size);
341462306a36Sopenharmony_ci		e2 = Add2Ptr(e1, esize);
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci		memmove(e1, e2, PtrOffset(e2, Add2Ptr(hdr, used)));
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci		le32_sub_cpu(&attr->res.data_size, esize);
341962306a36Sopenharmony_ci		hdr->used = cpu_to_le32(used - esize);
342062306a36Sopenharmony_ci		le32_sub_cpu(&hdr->total, esize);
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci		change_attr_size(rec, attr, le32_to_cpu(attr->size) - esize);
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci		mi->dirty = true;
342562306a36Sopenharmony_ci		break;
342662306a36Sopenharmony_ci
342762306a36Sopenharmony_ci	case SetIndexEntryVcnRoot:
342862306a36Sopenharmony_ci		root = resident_data(attr);
342962306a36Sopenharmony_ci		hdr = &root->ihdr;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci		if (!check_if_index_root(rec, lrh) ||
343262306a36Sopenharmony_ci		    !check_if_root_index(attr, hdr, lrh)) {
343362306a36Sopenharmony_ci			goto dirty_vol;
343462306a36Sopenharmony_ci		}
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci		e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci		de_set_vbn_le(e, *(__le64 *)data);
343962306a36Sopenharmony_ci		mi->dirty = true;
344062306a36Sopenharmony_ci		break;
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci	case UpdateFileNameRoot:
344362306a36Sopenharmony_ci		root = resident_data(attr);
344462306a36Sopenharmony_ci		hdr = &root->ihdr;
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci		if (!check_if_index_root(rec, lrh) ||
344762306a36Sopenharmony_ci		    !check_if_root_index(attr, hdr, lrh)) {
344862306a36Sopenharmony_ci			goto dirty_vol;
344962306a36Sopenharmony_ci		}
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci		e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
345262306a36Sopenharmony_ci		fname = (struct ATTR_FILE_NAME *)(e + 1);
345362306a36Sopenharmony_ci		memmove(&fname->dup, data, sizeof(fname->dup)); //
345462306a36Sopenharmony_ci		mi->dirty = true;
345562306a36Sopenharmony_ci		break;
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	case UpdateRecordDataRoot:
345862306a36Sopenharmony_ci		root = resident_data(attr);
345962306a36Sopenharmony_ci		hdr = &root->ihdr;
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci		if (!check_if_index_root(rec, lrh) ||
346262306a36Sopenharmony_ci		    !check_if_root_index(attr, hdr, lrh)) {
346362306a36Sopenharmony_ci			goto dirty_vol;
346462306a36Sopenharmony_ci		}
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ci		e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci		memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen);
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci		mi->dirty = true;
347162306a36Sopenharmony_ci		break;
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	case ZeroEndOfFileRecord:
347462306a36Sopenharmony_ci		if (roff + dlen > record_size)
347562306a36Sopenharmony_ci			goto dirty_vol;
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci		memset(attr, 0, dlen);
347862306a36Sopenharmony_ci		mi->dirty = true;
347962306a36Sopenharmony_ci		break;
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	case UpdateNonresidentValue:
348262306a36Sopenharmony_ci		if (lco < cbo + roff + dlen)
348362306a36Sopenharmony_ci			goto dirty_vol;
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci		memcpy(Add2Ptr(buffer_le, roff), data, dlen);
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci		a_dirty = true;
348862306a36Sopenharmony_ci		if (attr->type == ATTR_ALLOC)
348962306a36Sopenharmony_ci			ntfs_fix_pre_write(buffer_le, bytes);
349062306a36Sopenharmony_ci		break;
349162306a36Sopenharmony_ci
349262306a36Sopenharmony_ci	case AddIndexEntryAllocation:
349362306a36Sopenharmony_ci		ib = Add2Ptr(buffer_le, roff);
349462306a36Sopenharmony_ci		hdr = &ib->ihdr;
349562306a36Sopenharmony_ci		e = data;
349662306a36Sopenharmony_ci		esize = le16_to_cpu(e->size);
349762306a36Sopenharmony_ci		e1 = Add2Ptr(ib, aoff);
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci		if (is_baad(&ib->rhdr))
350062306a36Sopenharmony_ci			goto dirty_vol;
350162306a36Sopenharmony_ci		if (!check_lsn(&ib->rhdr, rlsn))
350262306a36Sopenharmony_ci			goto out;
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_ci		used = le32_to_cpu(hdr->used);
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci		if (!check_index_buffer(ib, bytes) ||
350762306a36Sopenharmony_ci		    !check_if_alloc_index(hdr, aoff) ||
350862306a36Sopenharmony_ci		    Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) ||
350962306a36Sopenharmony_ci		    used + esize > le32_to_cpu(hdr->total)) {
351062306a36Sopenharmony_ci			goto dirty_vol;
351162306a36Sopenharmony_ci		}
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci		memmove(Add2Ptr(e1, esize), e1,
351462306a36Sopenharmony_ci			PtrOffset(e1, Add2Ptr(hdr, used)));
351562306a36Sopenharmony_ci		memcpy(e1, e, esize);
351662306a36Sopenharmony_ci
351762306a36Sopenharmony_ci		hdr->used = cpu_to_le32(used + esize);
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci		a_dirty = true;
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_ci		ntfs_fix_pre_write(&ib->rhdr, bytes);
352262306a36Sopenharmony_ci		break;
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci	case DeleteIndexEntryAllocation:
352562306a36Sopenharmony_ci		ib = Add2Ptr(buffer_le, roff);
352662306a36Sopenharmony_ci		hdr = &ib->ihdr;
352762306a36Sopenharmony_ci		e = Add2Ptr(ib, aoff);
352862306a36Sopenharmony_ci		esize = le16_to_cpu(e->size);
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_ci		if (is_baad(&ib->rhdr))
353162306a36Sopenharmony_ci			goto dirty_vol;
353262306a36Sopenharmony_ci		if (!check_lsn(&ib->rhdr, rlsn))
353362306a36Sopenharmony_ci			goto out;
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci		if (!check_index_buffer(ib, bytes) ||
353662306a36Sopenharmony_ci		    !check_if_alloc_index(hdr, aoff)) {
353762306a36Sopenharmony_ci			goto dirty_vol;
353862306a36Sopenharmony_ci		}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci		e1 = Add2Ptr(e, esize);
354162306a36Sopenharmony_ci		nsize = esize;
354262306a36Sopenharmony_ci		used = le32_to_cpu(hdr->used);
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci		memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used)));
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci		hdr->used = cpu_to_le32(used - nsize);
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci		a_dirty = true;
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci		ntfs_fix_pre_write(&ib->rhdr, bytes);
355162306a36Sopenharmony_ci		break;
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci	case WriteEndOfIndexBuffer:
355462306a36Sopenharmony_ci		ib = Add2Ptr(buffer_le, roff);
355562306a36Sopenharmony_ci		hdr = &ib->ihdr;
355662306a36Sopenharmony_ci		e = Add2Ptr(ib, aoff);
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci		if (is_baad(&ib->rhdr))
355962306a36Sopenharmony_ci			goto dirty_vol;
356062306a36Sopenharmony_ci		if (!check_lsn(&ib->rhdr, rlsn))
356162306a36Sopenharmony_ci			goto out;
356262306a36Sopenharmony_ci		if (!check_index_buffer(ib, bytes) ||
356362306a36Sopenharmony_ci		    !check_if_alloc_index(hdr, aoff) ||
356462306a36Sopenharmony_ci		    aoff + dlen > offsetof(struct INDEX_BUFFER, ihdr) +
356562306a36Sopenharmony_ci					  le32_to_cpu(hdr->total)) {
356662306a36Sopenharmony_ci			goto dirty_vol;
356762306a36Sopenharmony_ci		}
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci		hdr->used = cpu_to_le32(dlen + PtrOffset(hdr, e));
357062306a36Sopenharmony_ci		memmove(e, data, dlen);
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci		a_dirty = true;
357362306a36Sopenharmony_ci		ntfs_fix_pre_write(&ib->rhdr, bytes);
357462306a36Sopenharmony_ci		break;
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci	case SetIndexEntryVcnAllocation:
357762306a36Sopenharmony_ci		ib = Add2Ptr(buffer_le, roff);
357862306a36Sopenharmony_ci		hdr = &ib->ihdr;
357962306a36Sopenharmony_ci		e = Add2Ptr(ib, aoff);
358062306a36Sopenharmony_ci
358162306a36Sopenharmony_ci		if (is_baad(&ib->rhdr))
358262306a36Sopenharmony_ci			goto dirty_vol;
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci		if (!check_lsn(&ib->rhdr, rlsn))
358562306a36Sopenharmony_ci			goto out;
358662306a36Sopenharmony_ci		if (!check_index_buffer(ib, bytes) ||
358762306a36Sopenharmony_ci		    !check_if_alloc_index(hdr, aoff)) {
358862306a36Sopenharmony_ci			goto dirty_vol;
358962306a36Sopenharmony_ci		}
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci		de_set_vbn_le(e, *(__le64 *)data);
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci		a_dirty = true;
359462306a36Sopenharmony_ci		ntfs_fix_pre_write(&ib->rhdr, bytes);
359562306a36Sopenharmony_ci		break;
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci	case UpdateFileNameAllocation:
359862306a36Sopenharmony_ci		ib = Add2Ptr(buffer_le, roff);
359962306a36Sopenharmony_ci		hdr = &ib->ihdr;
360062306a36Sopenharmony_ci		e = Add2Ptr(ib, aoff);
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci		if (is_baad(&ib->rhdr))
360362306a36Sopenharmony_ci			goto dirty_vol;
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci		if (!check_lsn(&ib->rhdr, rlsn))
360662306a36Sopenharmony_ci			goto out;
360762306a36Sopenharmony_ci		if (!check_index_buffer(ib, bytes) ||
360862306a36Sopenharmony_ci		    !check_if_alloc_index(hdr, aoff)) {
360962306a36Sopenharmony_ci			goto dirty_vol;
361062306a36Sopenharmony_ci		}
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci		fname = (struct ATTR_FILE_NAME *)(e + 1);
361362306a36Sopenharmony_ci		memmove(&fname->dup, data, sizeof(fname->dup));
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci		a_dirty = true;
361662306a36Sopenharmony_ci		ntfs_fix_pre_write(&ib->rhdr, bytes);
361762306a36Sopenharmony_ci		break;
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci	case SetBitsInNonresidentBitMap:
362062306a36Sopenharmony_ci		off = le32_to_cpu(((struct BITMAP_RANGE *)data)->bitmap_off);
362162306a36Sopenharmony_ci		bits = le32_to_cpu(((struct BITMAP_RANGE *)data)->bits);
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci		if (cbo + (off + 7) / 8 > lco ||
362462306a36Sopenharmony_ci		    cbo + ((off + bits + 7) / 8) > lco) {
362562306a36Sopenharmony_ci			goto dirty_vol;
362662306a36Sopenharmony_ci		}
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci		ntfs_bitmap_set_le(Add2Ptr(buffer_le, roff), off, bits);
362962306a36Sopenharmony_ci		a_dirty = true;
363062306a36Sopenharmony_ci		break;
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci	case ClearBitsInNonresidentBitMap:
363362306a36Sopenharmony_ci		off = le32_to_cpu(((struct BITMAP_RANGE *)data)->bitmap_off);
363462306a36Sopenharmony_ci		bits = le32_to_cpu(((struct BITMAP_RANGE *)data)->bits);
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci		if (cbo + (off + 7) / 8 > lco ||
363762306a36Sopenharmony_ci		    cbo + ((off + bits + 7) / 8) > lco) {
363862306a36Sopenharmony_ci			goto dirty_vol;
363962306a36Sopenharmony_ci		}
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci		ntfs_bitmap_clear_le(Add2Ptr(buffer_le, roff), off, bits);
364262306a36Sopenharmony_ci		a_dirty = true;
364362306a36Sopenharmony_ci		break;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	case UpdateRecordDataAllocation:
364662306a36Sopenharmony_ci		ib = Add2Ptr(buffer_le, roff);
364762306a36Sopenharmony_ci		hdr = &ib->ihdr;
364862306a36Sopenharmony_ci		e = Add2Ptr(ib, aoff);
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci		if (is_baad(&ib->rhdr))
365162306a36Sopenharmony_ci			goto dirty_vol;
365262306a36Sopenharmony_ci
365362306a36Sopenharmony_ci		if (!check_lsn(&ib->rhdr, rlsn))
365462306a36Sopenharmony_ci			goto out;
365562306a36Sopenharmony_ci		if (!check_index_buffer(ib, bytes) ||
365662306a36Sopenharmony_ci		    !check_if_alloc_index(hdr, aoff)) {
365762306a36Sopenharmony_ci			goto dirty_vol;
365862306a36Sopenharmony_ci		}
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci		memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen);
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci		a_dirty = true;
366362306a36Sopenharmony_ci		ntfs_fix_pre_write(&ib->rhdr, bytes);
366462306a36Sopenharmony_ci		break;
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	default:
366762306a36Sopenharmony_ci		WARN_ON(1);
366862306a36Sopenharmony_ci	}
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci	if (rlsn) {
367162306a36Sopenharmony_ci		__le64 t64 = cpu_to_le64(*rlsn);
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci		if (rec)
367462306a36Sopenharmony_ci			rec->rhdr.lsn = t64;
367562306a36Sopenharmony_ci		if (ib)
367662306a36Sopenharmony_ci			ib->rhdr.lsn = t64;
367762306a36Sopenharmony_ci	}
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci	if (mi && mi->dirty) {
368062306a36Sopenharmony_ci		err = mi_write(mi, 0);
368162306a36Sopenharmony_ci		if (err)
368262306a36Sopenharmony_ci			goto out;
368362306a36Sopenharmony_ci	}
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	if (a_dirty) {
368662306a36Sopenharmony_ci		attr = oa->attr;
368762306a36Sopenharmony_ci		err = ntfs_sb_write_run(sbi, oa->run1, vbo, buffer_le, bytes,
368862306a36Sopenharmony_ci					0);
368962306a36Sopenharmony_ci		if (err)
369062306a36Sopenharmony_ci			goto out;
369162306a36Sopenharmony_ci	}
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_ciout:
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci	if (inode)
369662306a36Sopenharmony_ci		iput(inode);
369762306a36Sopenharmony_ci	else if (mi != mi2_child)
369862306a36Sopenharmony_ci		mi_put(mi);
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci	kfree(buffer_le);
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci	return err;
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_cidirty_vol:
370562306a36Sopenharmony_ci	log->set_dirty = true;
370662306a36Sopenharmony_ci	goto out;
370762306a36Sopenharmony_ci}
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci/*
371062306a36Sopenharmony_ci * log_replay - Replays log and empties it.
371162306a36Sopenharmony_ci *
371262306a36Sopenharmony_ci * This function is called during mount operation.
371362306a36Sopenharmony_ci * It replays log and empties it.
371462306a36Sopenharmony_ci * Initialized is set false if logfile contains '-1'.
371562306a36Sopenharmony_ci */
371662306a36Sopenharmony_ciint log_replay(struct ntfs_inode *ni, bool *initialized)
371762306a36Sopenharmony_ci{
371862306a36Sopenharmony_ci	int err;
371962306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = ni->mi.sbi;
372062306a36Sopenharmony_ci	struct ntfs_log *log;
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci	u64 rec_lsn, checkpt_lsn = 0, rlsn = 0;
372362306a36Sopenharmony_ci	struct ATTR_NAME_ENTRY *attr_names = NULL;
372462306a36Sopenharmony_ci	struct RESTART_TABLE *dptbl = NULL;
372562306a36Sopenharmony_ci	struct RESTART_TABLE *trtbl = NULL;
372662306a36Sopenharmony_ci	const struct RESTART_TABLE *rt;
372762306a36Sopenharmony_ci	struct RESTART_TABLE *oatbl = NULL;
372862306a36Sopenharmony_ci	struct inode *inode;
372962306a36Sopenharmony_ci	struct OpenAttr *oa;
373062306a36Sopenharmony_ci	struct ntfs_inode *ni_oe;
373162306a36Sopenharmony_ci	struct ATTRIB *attr = NULL;
373262306a36Sopenharmony_ci	u64 size, vcn, undo_next_lsn;
373362306a36Sopenharmony_ci	CLST rno, lcn, lcn0, len0, clen;
373462306a36Sopenharmony_ci	void *data;
373562306a36Sopenharmony_ci	struct NTFS_RESTART *rst = NULL;
373662306a36Sopenharmony_ci	struct lcb *lcb = NULL;
373762306a36Sopenharmony_ci	struct OPEN_ATTR_ENRTY *oe;
373862306a36Sopenharmony_ci	struct TRANSACTION_ENTRY *tr;
373962306a36Sopenharmony_ci	struct DIR_PAGE_ENTRY *dp;
374062306a36Sopenharmony_ci	u32 i, bytes_per_attr_entry;
374162306a36Sopenharmony_ci	u32 vbo, tail, off, dlen;
374262306a36Sopenharmony_ci	u32 saved_len, rec_len, transact_id;
374362306a36Sopenharmony_ci	bool use_second_page;
374462306a36Sopenharmony_ci	struct RESTART_AREA *ra2, *ra = NULL;
374562306a36Sopenharmony_ci	struct CLIENT_REC *ca, *cr;
374662306a36Sopenharmony_ci	__le16 client;
374762306a36Sopenharmony_ci	struct RESTART_HDR *rh;
374862306a36Sopenharmony_ci	const struct LFS_RECORD_HDR *frh;
374962306a36Sopenharmony_ci	const struct LOG_REC_HDR *lrh;
375062306a36Sopenharmony_ci	bool is_mapped;
375162306a36Sopenharmony_ci	bool is_ro = sb_rdonly(sbi->sb);
375262306a36Sopenharmony_ci	u64 t64;
375362306a36Sopenharmony_ci	u16 t16;
375462306a36Sopenharmony_ci	u32 t32;
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci	log = kzalloc(sizeof(struct ntfs_log), GFP_NOFS);
375762306a36Sopenharmony_ci	if (!log)
375862306a36Sopenharmony_ci		return -ENOMEM;
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci	log->ni = ni;
376162306a36Sopenharmony_ci	log->l_size = log->orig_file_size = ni->vfs_inode.i_size;
376262306a36Sopenharmony_ci
376362306a36Sopenharmony_ci	/* Get the size of page. NOTE: To replay we can use default page. */
376462306a36Sopenharmony_ci#if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2
376562306a36Sopenharmony_ci	log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, true);
376662306a36Sopenharmony_ci#else
376762306a36Sopenharmony_ci	log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, false);
376862306a36Sopenharmony_ci#endif
376962306a36Sopenharmony_ci	if (!log->page_size) {
377062306a36Sopenharmony_ci		err = -EINVAL;
377162306a36Sopenharmony_ci		goto out;
377262306a36Sopenharmony_ci	}
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	log->one_page_buf = kmalloc(log->page_size, GFP_NOFS);
377562306a36Sopenharmony_ci	if (!log->one_page_buf) {
377662306a36Sopenharmony_ci		err = -ENOMEM;
377762306a36Sopenharmony_ci		goto out;
377862306a36Sopenharmony_ci	}
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	log->page_mask = log->page_size - 1;
378162306a36Sopenharmony_ci	log->page_bits = blksize_bits(log->page_size);
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	/* Look for a restart area on the disk. */
378462306a36Sopenharmony_ci	err = log_read_rst(log, true, &log->rst_info);
378562306a36Sopenharmony_ci	if (err)
378662306a36Sopenharmony_ci		goto out;
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_ci	/* remember 'initialized' */
378962306a36Sopenharmony_ci	*initialized = log->rst_info.initialized;
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci	if (!log->rst_info.restart) {
379262306a36Sopenharmony_ci		if (log->rst_info.initialized) {
379362306a36Sopenharmony_ci			/* No restart area but the file is not initialized. */
379462306a36Sopenharmony_ci			err = -EINVAL;
379562306a36Sopenharmony_ci			goto out;
379662306a36Sopenharmony_ci		}
379762306a36Sopenharmony_ci
379862306a36Sopenharmony_ci		log_init_pg_hdr(log, 1, 1);
379962306a36Sopenharmony_ci		log_create(log, 0, get_random_u32(), false, false);
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci		ra = log_create_ra(log);
380262306a36Sopenharmony_ci		if (!ra) {
380362306a36Sopenharmony_ci			err = -ENOMEM;
380462306a36Sopenharmony_ci			goto out;
380562306a36Sopenharmony_ci		}
380662306a36Sopenharmony_ci		log->ra = ra;
380762306a36Sopenharmony_ci		log->init_ra = true;
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci		goto process_log;
381062306a36Sopenharmony_ci	}
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci	/*
381362306a36Sopenharmony_ci	 * If the restart offset above wasn't zero then we won't
381462306a36Sopenharmony_ci	 * look for a second restart.
381562306a36Sopenharmony_ci	 */
381662306a36Sopenharmony_ci	if (log->rst_info.vbo)
381762306a36Sopenharmony_ci		goto check_restart_area;
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ci	err = log_read_rst(log, false, &log->rst_info2);
382062306a36Sopenharmony_ci	if (err)
382162306a36Sopenharmony_ci		goto out;
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	/* Determine which restart area to use. */
382462306a36Sopenharmony_ci	if (!log->rst_info2.restart ||
382562306a36Sopenharmony_ci	    log->rst_info2.last_lsn <= log->rst_info.last_lsn)
382662306a36Sopenharmony_ci		goto use_first_page;
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	use_second_page = true;
382962306a36Sopenharmony_ci
383062306a36Sopenharmony_ci	if (log->rst_info.chkdsk_was_run &&
383162306a36Sopenharmony_ci	    log->page_size != log->rst_info.vbo) {
383262306a36Sopenharmony_ci		struct RECORD_PAGE_HDR *sp = NULL;
383362306a36Sopenharmony_ci		bool usa_error;
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci		if (!read_log_page(log, log->page_size, &sp, &usa_error) &&
383662306a36Sopenharmony_ci		    sp->rhdr.sign == NTFS_CHKD_SIGNATURE) {
383762306a36Sopenharmony_ci			use_second_page = false;
383862306a36Sopenharmony_ci		}
383962306a36Sopenharmony_ci		kfree(sp);
384062306a36Sopenharmony_ci	}
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci	if (use_second_page) {
384362306a36Sopenharmony_ci		kfree(log->rst_info.r_page);
384462306a36Sopenharmony_ci		memcpy(&log->rst_info, &log->rst_info2,
384562306a36Sopenharmony_ci		       sizeof(struct restart_info));
384662306a36Sopenharmony_ci		log->rst_info2.r_page = NULL;
384762306a36Sopenharmony_ci	}
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_ciuse_first_page:
385062306a36Sopenharmony_ci	kfree(log->rst_info2.r_page);
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_cicheck_restart_area:
385362306a36Sopenharmony_ci	/*
385462306a36Sopenharmony_ci	 * If the restart area is at offset 0, we want
385562306a36Sopenharmony_ci	 * to write the second restart area first.
385662306a36Sopenharmony_ci	 */
385762306a36Sopenharmony_ci	log->init_ra = !!log->rst_info.vbo;
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci	/* If we have a valid page then grab a pointer to the restart area. */
386062306a36Sopenharmony_ci	ra2 = log->rst_info.valid_page ?
386162306a36Sopenharmony_ci		      Add2Ptr(log->rst_info.r_page,
386262306a36Sopenharmony_ci			      le16_to_cpu(log->rst_info.r_page->ra_off)) :
386362306a36Sopenharmony_ci		      NULL;
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci	if (log->rst_info.chkdsk_was_run ||
386662306a36Sopenharmony_ci	    (ra2 && ra2->client_idx[1] == LFS_NO_CLIENT_LE)) {
386762306a36Sopenharmony_ci		bool wrapped = false;
386862306a36Sopenharmony_ci		bool use_multi_page = false;
386962306a36Sopenharmony_ci		u32 open_log_count;
387062306a36Sopenharmony_ci
387162306a36Sopenharmony_ci		/* Do some checks based on whether we have a valid log page. */
387262306a36Sopenharmony_ci		open_log_count = log->rst_info.valid_page ?
387362306a36Sopenharmony_ci					 le32_to_cpu(ra2->open_log_count) :
387462306a36Sopenharmony_ci					 get_random_u32();
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci		log_init_pg_hdr(log, 1, 1);
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci		log_create(log, log->rst_info.last_lsn, open_log_count, wrapped,
387962306a36Sopenharmony_ci			   use_multi_page);
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_ci		ra = log_create_ra(log);
388262306a36Sopenharmony_ci		if (!ra) {
388362306a36Sopenharmony_ci			err = -ENOMEM;
388462306a36Sopenharmony_ci			goto out;
388562306a36Sopenharmony_ci		}
388662306a36Sopenharmony_ci		log->ra = ra;
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_ci		/* Put the restart areas and initialize
388962306a36Sopenharmony_ci		 * the log file as required.
389062306a36Sopenharmony_ci		 */
389162306a36Sopenharmony_ci		goto process_log;
389262306a36Sopenharmony_ci	}
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	if (!ra2) {
389562306a36Sopenharmony_ci		err = -EINVAL;
389662306a36Sopenharmony_ci		goto out;
389762306a36Sopenharmony_ci	}
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	/*
390062306a36Sopenharmony_ci	 * If the log page or the system page sizes have changed, we can't
390162306a36Sopenharmony_ci	 * use the log file. We must use the system page size instead of the
390262306a36Sopenharmony_ci	 * default size if there is not a clean shutdown.
390362306a36Sopenharmony_ci	 */
390462306a36Sopenharmony_ci	t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size);
390562306a36Sopenharmony_ci	if (log->page_size != t32) {
390662306a36Sopenharmony_ci		log->l_size = log->orig_file_size;
390762306a36Sopenharmony_ci		log->page_size = norm_file_page(t32, &log->l_size,
390862306a36Sopenharmony_ci						t32 == DefaultLogPageSize);
390962306a36Sopenharmony_ci	}
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	if (log->page_size != t32 ||
391262306a36Sopenharmony_ci	    log->page_size != le32_to_cpu(log->rst_info.r_page->page_size)) {
391362306a36Sopenharmony_ci		err = -EINVAL;
391462306a36Sopenharmony_ci		goto out;
391562306a36Sopenharmony_ci	}
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci	/* If the file size has shrunk then we won't mount it. */
391862306a36Sopenharmony_ci	if (log->l_size < le64_to_cpu(ra2->l_size)) {
391962306a36Sopenharmony_ci		err = -EINVAL;
392062306a36Sopenharmony_ci		goto out;
392162306a36Sopenharmony_ci	}
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	log_init_pg_hdr(log, le16_to_cpu(log->rst_info.r_page->major_ver),
392462306a36Sopenharmony_ci			le16_to_cpu(log->rst_info.r_page->minor_ver));
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	log->l_size = le64_to_cpu(ra2->l_size);
392762306a36Sopenharmony_ci	log->seq_num_bits = le32_to_cpu(ra2->seq_num_bits);
392862306a36Sopenharmony_ci	log->file_data_bits = sizeof(u64) * 8 - log->seq_num_bits;
392962306a36Sopenharmony_ci	log->seq_num_mask = (8 << log->file_data_bits) - 1;
393062306a36Sopenharmony_ci	log->last_lsn = le64_to_cpu(ra2->current_lsn);
393162306a36Sopenharmony_ci	log->seq_num = log->last_lsn >> log->file_data_bits;
393262306a36Sopenharmony_ci	log->ra_off = le16_to_cpu(log->rst_info.r_page->ra_off);
393362306a36Sopenharmony_ci	log->restart_size = log->sys_page_size - log->ra_off;
393462306a36Sopenharmony_ci	log->record_header_len = le16_to_cpu(ra2->rec_hdr_len);
393562306a36Sopenharmony_ci	log->ra_size = le16_to_cpu(ra2->ra_len);
393662306a36Sopenharmony_ci	log->data_off = le16_to_cpu(ra2->data_off);
393762306a36Sopenharmony_ci	log->data_size = log->page_size - log->data_off;
393862306a36Sopenharmony_ci	log->reserved = log->data_size - log->record_header_len;
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci	vbo = lsn_to_vbo(log, log->last_lsn);
394162306a36Sopenharmony_ci
394262306a36Sopenharmony_ci	if (vbo < log->first_page) {
394362306a36Sopenharmony_ci		/* This is a pseudo lsn. */
394462306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_NO_LAST_LSN;
394562306a36Sopenharmony_ci		log->next_page = log->first_page;
394662306a36Sopenharmony_ci		goto find_oldest;
394762306a36Sopenharmony_ci	}
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	/* Find the end of this log record. */
395062306a36Sopenharmony_ci	off = final_log_off(log, log->last_lsn,
395162306a36Sopenharmony_ci			    le32_to_cpu(ra2->last_lsn_data_len));
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ci	/* If we wrapped the file then increment the sequence number. */
395462306a36Sopenharmony_ci	if (off <= vbo) {
395562306a36Sopenharmony_ci		log->seq_num += 1;
395662306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_WRAPPED;
395762306a36Sopenharmony_ci	}
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	/* Now compute the next log page to use. */
396062306a36Sopenharmony_ci	vbo &= ~log->sys_page_mask;
396162306a36Sopenharmony_ci	tail = log->page_size - (off & log->page_mask) - 1;
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_ci	/*
396462306a36Sopenharmony_ci	 *If we can fit another log record on the page,
396562306a36Sopenharmony_ci	 * move back a page the log file.
396662306a36Sopenharmony_ci	 */
396762306a36Sopenharmony_ci	if (tail >= log->record_header_len) {
396862306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_REUSE_TAIL;
396962306a36Sopenharmony_ci		log->next_page = vbo;
397062306a36Sopenharmony_ci	} else {
397162306a36Sopenharmony_ci		log->next_page = next_page_off(log, vbo);
397262306a36Sopenharmony_ci	}
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_cifind_oldest:
397562306a36Sopenharmony_ci	/*
397662306a36Sopenharmony_ci	 * Find the oldest client lsn. Use the last
397762306a36Sopenharmony_ci	 * flushed lsn as a starting point.
397862306a36Sopenharmony_ci	 */
397962306a36Sopenharmony_ci	log->oldest_lsn = log->last_lsn;
398062306a36Sopenharmony_ci	oldest_client_lsn(Add2Ptr(ra2, le16_to_cpu(ra2->client_off)),
398162306a36Sopenharmony_ci			  ra2->client_idx[1], &log->oldest_lsn);
398262306a36Sopenharmony_ci	log->oldest_lsn_off = lsn_to_vbo(log, log->oldest_lsn);
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	if (log->oldest_lsn_off < log->first_page)
398562306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_NO_OLDEST_LSN;
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci	if (!(ra2->flags & RESTART_SINGLE_PAGE_IO))
398862306a36Sopenharmony_ci		log->l_flags |= NTFSLOG_WRAPPED | NTFSLOG_MULTIPLE_PAGE_IO;
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci	log->current_openlog_count = le32_to_cpu(ra2->open_log_count);
399162306a36Sopenharmony_ci	log->total_avail_pages = log->l_size - log->first_page;
399262306a36Sopenharmony_ci	log->total_avail = log->total_avail_pages >> log->page_bits;
399362306a36Sopenharmony_ci	log->max_current_avail = log->total_avail * log->reserved;
399462306a36Sopenharmony_ci	log->total_avail = log->total_avail * log->data_size;
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_ci	log->current_avail = current_log_avail(log);
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	ra = kzalloc(log->restart_size, GFP_NOFS);
399962306a36Sopenharmony_ci	if (!ra) {
400062306a36Sopenharmony_ci		err = -ENOMEM;
400162306a36Sopenharmony_ci		goto out;
400262306a36Sopenharmony_ci	}
400362306a36Sopenharmony_ci	log->ra = ra;
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci	t16 = le16_to_cpu(ra2->client_off);
400662306a36Sopenharmony_ci	if (t16 == offsetof(struct RESTART_AREA, clients)) {
400762306a36Sopenharmony_ci		memcpy(ra, ra2, log->ra_size);
400862306a36Sopenharmony_ci	} else {
400962306a36Sopenharmony_ci		memcpy(ra, ra2, offsetof(struct RESTART_AREA, clients));
401062306a36Sopenharmony_ci		memcpy(ra->clients, Add2Ptr(ra2, t16),
401162306a36Sopenharmony_ci		       le16_to_cpu(ra2->ra_len) - t16);
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ci		log->current_openlog_count = get_random_u32();
401462306a36Sopenharmony_ci		ra->open_log_count = cpu_to_le32(log->current_openlog_count);
401562306a36Sopenharmony_ci		log->ra_size = offsetof(struct RESTART_AREA, clients) +
401662306a36Sopenharmony_ci			       sizeof(struct CLIENT_REC);
401762306a36Sopenharmony_ci		ra->client_off =
401862306a36Sopenharmony_ci			cpu_to_le16(offsetof(struct RESTART_AREA, clients));
401962306a36Sopenharmony_ci		ra->ra_len = cpu_to_le16(log->ra_size);
402062306a36Sopenharmony_ci	}
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci	le32_add_cpu(&ra->open_log_count, 1);
402362306a36Sopenharmony_ci
402462306a36Sopenharmony_ci	/* Now we need to walk through looking for the last lsn. */
402562306a36Sopenharmony_ci	err = last_log_lsn(log);
402662306a36Sopenharmony_ci	if (err)
402762306a36Sopenharmony_ci		goto out;
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci	log->current_avail = current_log_avail(log);
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_ci	/* Remember which restart area to write first. */
403262306a36Sopenharmony_ci	log->init_ra = log->rst_info.vbo;
403362306a36Sopenharmony_ci
403462306a36Sopenharmony_ciprocess_log:
403562306a36Sopenharmony_ci	/* 1.0, 1.1, 2.0 log->major_ver/minor_ver - short values. */
403662306a36Sopenharmony_ci	switch ((log->major_ver << 16) + log->minor_ver) {
403762306a36Sopenharmony_ci	case 0x10000:
403862306a36Sopenharmony_ci	case 0x10001:
403962306a36Sopenharmony_ci	case 0x20000:
404062306a36Sopenharmony_ci		break;
404162306a36Sopenharmony_ci	default:
404262306a36Sopenharmony_ci		ntfs_warn(sbi->sb, "\x24LogFile version %d.%d is not supported",
404362306a36Sopenharmony_ci			  log->major_ver, log->minor_ver);
404462306a36Sopenharmony_ci		err = -EOPNOTSUPP;
404562306a36Sopenharmony_ci		log->set_dirty = true;
404662306a36Sopenharmony_ci		goto out;
404762306a36Sopenharmony_ci	}
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	/* One client "NTFS" per logfile. */
405062306a36Sopenharmony_ci	ca = Add2Ptr(ra, le16_to_cpu(ra->client_off));
405162306a36Sopenharmony_ci
405262306a36Sopenharmony_ci	for (client = ra->client_idx[1];; client = cr->next_client) {
405362306a36Sopenharmony_ci		if (client == LFS_NO_CLIENT_LE) {
405462306a36Sopenharmony_ci			/* Insert "NTFS" client LogFile. */
405562306a36Sopenharmony_ci			client = ra->client_idx[0];
405662306a36Sopenharmony_ci			if (client == LFS_NO_CLIENT_LE) {
405762306a36Sopenharmony_ci				err = -EINVAL;
405862306a36Sopenharmony_ci				goto out;
405962306a36Sopenharmony_ci			}
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci			t16 = le16_to_cpu(client);
406262306a36Sopenharmony_ci			cr = ca + t16;
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_ci			remove_client(ca, cr, &ra->client_idx[0]);
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci			cr->restart_lsn = 0;
406762306a36Sopenharmony_ci			cr->oldest_lsn = cpu_to_le64(log->oldest_lsn);
406862306a36Sopenharmony_ci			cr->name_bytes = cpu_to_le32(8);
406962306a36Sopenharmony_ci			cr->name[0] = cpu_to_le16('N');
407062306a36Sopenharmony_ci			cr->name[1] = cpu_to_le16('T');
407162306a36Sopenharmony_ci			cr->name[2] = cpu_to_le16('F');
407262306a36Sopenharmony_ci			cr->name[3] = cpu_to_le16('S');
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci			add_client(ca, t16, &ra->client_idx[1]);
407562306a36Sopenharmony_ci			break;
407662306a36Sopenharmony_ci		}
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_ci		cr = ca + le16_to_cpu(client);
407962306a36Sopenharmony_ci
408062306a36Sopenharmony_ci		if (cpu_to_le32(8) == cr->name_bytes &&
408162306a36Sopenharmony_ci		    cpu_to_le16('N') == cr->name[0] &&
408262306a36Sopenharmony_ci		    cpu_to_le16('T') == cr->name[1] &&
408362306a36Sopenharmony_ci		    cpu_to_le16('F') == cr->name[2] &&
408462306a36Sopenharmony_ci		    cpu_to_le16('S') == cr->name[3])
408562306a36Sopenharmony_ci			break;
408662306a36Sopenharmony_ci	}
408762306a36Sopenharmony_ci
408862306a36Sopenharmony_ci	/* Update the client handle with the client block information. */
408962306a36Sopenharmony_ci	log->client_id.seq_num = cr->seq_num;
409062306a36Sopenharmony_ci	log->client_id.client_idx = client;
409162306a36Sopenharmony_ci
409262306a36Sopenharmony_ci	err = read_rst_area(log, &rst, &checkpt_lsn);
409362306a36Sopenharmony_ci	if (err)
409462306a36Sopenharmony_ci		goto out;
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci	if (!rst)
409762306a36Sopenharmony_ci		goto out;
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci	bytes_per_attr_entry = !rst->major_ver ? 0x2C : 0x28;
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci	if (rst->check_point_start)
410262306a36Sopenharmony_ci		checkpt_lsn = le64_to_cpu(rst->check_point_start);
410362306a36Sopenharmony_ci
410462306a36Sopenharmony_ci	/* Allocate and Read the Transaction Table. */
410562306a36Sopenharmony_ci	if (!rst->transact_table_len)
410662306a36Sopenharmony_ci		goto check_dirty_page_table;
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci	t64 = le64_to_cpu(rst->transact_table_lsn);
410962306a36Sopenharmony_ci	err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb);
411062306a36Sopenharmony_ci	if (err)
411162306a36Sopenharmony_ci		goto out;
411262306a36Sopenharmony_ci
411362306a36Sopenharmony_ci	lrh = lcb->log_rec;
411462306a36Sopenharmony_ci	frh = lcb->lrh;
411562306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
411662306a36Sopenharmony_ci
411762306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id),
411862306a36Sopenharmony_ci			   bytes_per_attr_entry)) {
411962306a36Sopenharmony_ci		err = -EINVAL;
412062306a36Sopenharmony_ci		goto out;
412162306a36Sopenharmony_ci	}
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->redo_off);
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci	rt = Add2Ptr(lrh, t16);
412662306a36Sopenharmony_ci	t32 = rec_len - t16;
412762306a36Sopenharmony_ci
412862306a36Sopenharmony_ci	/* Now check that this is a valid restart table. */
412962306a36Sopenharmony_ci	if (!check_rstbl(rt, t32)) {
413062306a36Sopenharmony_ci		err = -EINVAL;
413162306a36Sopenharmony_ci		goto out;
413262306a36Sopenharmony_ci	}
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	trtbl = kmemdup(rt, t32, GFP_NOFS);
413562306a36Sopenharmony_ci	if (!trtbl) {
413662306a36Sopenharmony_ci		err = -ENOMEM;
413762306a36Sopenharmony_ci		goto out;
413862306a36Sopenharmony_ci	}
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_ci	lcb_put(lcb);
414162306a36Sopenharmony_ci	lcb = NULL;
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_cicheck_dirty_page_table:
414462306a36Sopenharmony_ci	/* The next record back should be the Dirty Pages Table. */
414562306a36Sopenharmony_ci	if (!rst->dirty_pages_len)
414662306a36Sopenharmony_ci		goto check_attribute_names;
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci	t64 = le64_to_cpu(rst->dirty_pages_table_lsn);
414962306a36Sopenharmony_ci	err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb);
415062306a36Sopenharmony_ci	if (err)
415162306a36Sopenharmony_ci		goto out;
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	lrh = lcb->log_rec;
415462306a36Sopenharmony_ci	frh = lcb->lrh;
415562306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id),
415862306a36Sopenharmony_ci			   bytes_per_attr_entry)) {
415962306a36Sopenharmony_ci		err = -EINVAL;
416062306a36Sopenharmony_ci		goto out;
416162306a36Sopenharmony_ci	}
416262306a36Sopenharmony_ci
416362306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->redo_off);
416462306a36Sopenharmony_ci
416562306a36Sopenharmony_ci	rt = Add2Ptr(lrh, t16);
416662306a36Sopenharmony_ci	t32 = rec_len - t16;
416762306a36Sopenharmony_ci
416862306a36Sopenharmony_ci	/* Now check that this is a valid restart table. */
416962306a36Sopenharmony_ci	if (!check_rstbl(rt, t32)) {
417062306a36Sopenharmony_ci		err = -EINVAL;
417162306a36Sopenharmony_ci		goto out;
417262306a36Sopenharmony_ci	}
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci	dptbl = kmemdup(rt, t32, GFP_NOFS);
417562306a36Sopenharmony_ci	if (!dptbl) {
417662306a36Sopenharmony_ci		err = -ENOMEM;
417762306a36Sopenharmony_ci		goto out;
417862306a36Sopenharmony_ci	}
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	/* Convert Ra version '0' into version '1'. */
418162306a36Sopenharmony_ci	if (rst->major_ver)
418262306a36Sopenharmony_ci		goto end_conv_1;
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_ci	dp = NULL;
418562306a36Sopenharmony_ci	while ((dp = enum_rstbl(dptbl, dp))) {
418662306a36Sopenharmony_ci		struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp;
418762306a36Sopenharmony_ci		// NOTE: Danger. Check for of boundary.
418862306a36Sopenharmony_ci		memmove(&dp->vcn, &dp0->vcn_low,
418962306a36Sopenharmony_ci			2 * sizeof(u64) +
419062306a36Sopenharmony_ci				le32_to_cpu(dp->lcns_follow) * sizeof(u64));
419162306a36Sopenharmony_ci	}
419262306a36Sopenharmony_ci
419362306a36Sopenharmony_ciend_conv_1:
419462306a36Sopenharmony_ci	lcb_put(lcb);
419562306a36Sopenharmony_ci	lcb = NULL;
419662306a36Sopenharmony_ci
419762306a36Sopenharmony_ci	/*
419862306a36Sopenharmony_ci	 * Go through the table and remove the duplicates,
419962306a36Sopenharmony_ci	 * remembering the oldest lsn values.
420062306a36Sopenharmony_ci	 */
420162306a36Sopenharmony_ci	if (sbi->cluster_size <= log->page_size)
420262306a36Sopenharmony_ci		goto trace_dp_table;
420362306a36Sopenharmony_ci
420462306a36Sopenharmony_ci	dp = NULL;
420562306a36Sopenharmony_ci	while ((dp = enum_rstbl(dptbl, dp))) {
420662306a36Sopenharmony_ci		struct DIR_PAGE_ENTRY *next = dp;
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_ci		while ((next = enum_rstbl(dptbl, next))) {
420962306a36Sopenharmony_ci			if (next->target_attr == dp->target_attr &&
421062306a36Sopenharmony_ci			    next->vcn == dp->vcn) {
421162306a36Sopenharmony_ci				if (le64_to_cpu(next->oldest_lsn) <
421262306a36Sopenharmony_ci				    le64_to_cpu(dp->oldest_lsn)) {
421362306a36Sopenharmony_ci					dp->oldest_lsn = next->oldest_lsn;
421462306a36Sopenharmony_ci				}
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci				free_rsttbl_idx(dptbl, PtrOffset(dptbl, next));
421762306a36Sopenharmony_ci			}
421862306a36Sopenharmony_ci		}
421962306a36Sopenharmony_ci	}
422062306a36Sopenharmony_citrace_dp_table:
422162306a36Sopenharmony_cicheck_attribute_names:
422262306a36Sopenharmony_ci	/* The next record should be the Attribute Names. */
422362306a36Sopenharmony_ci	if (!rst->attr_names_len)
422462306a36Sopenharmony_ci		goto check_attr_table;
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	t64 = le64_to_cpu(rst->attr_names_lsn);
422762306a36Sopenharmony_ci	err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb);
422862306a36Sopenharmony_ci	if (err)
422962306a36Sopenharmony_ci		goto out;
423062306a36Sopenharmony_ci
423162306a36Sopenharmony_ci	lrh = lcb->log_rec;
423262306a36Sopenharmony_ci	frh = lcb->lrh;
423362306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
423462306a36Sopenharmony_ci
423562306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id),
423662306a36Sopenharmony_ci			   bytes_per_attr_entry)) {
423762306a36Sopenharmony_ci		err = -EINVAL;
423862306a36Sopenharmony_ci		goto out;
423962306a36Sopenharmony_ci	}
424062306a36Sopenharmony_ci
424162306a36Sopenharmony_ci	t32 = lrh_length(lrh);
424262306a36Sopenharmony_ci	rec_len -= t32;
424362306a36Sopenharmony_ci
424462306a36Sopenharmony_ci	attr_names = kmemdup(Add2Ptr(lrh, t32), rec_len, GFP_NOFS);
424562306a36Sopenharmony_ci	if (!attr_names) {
424662306a36Sopenharmony_ci		err = -ENOMEM;
424762306a36Sopenharmony_ci		goto out;
424862306a36Sopenharmony_ci	}
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_ci	lcb_put(lcb);
425162306a36Sopenharmony_ci	lcb = NULL;
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_cicheck_attr_table:
425462306a36Sopenharmony_ci	/* The next record should be the attribute Table. */
425562306a36Sopenharmony_ci	if (!rst->open_attr_len)
425662306a36Sopenharmony_ci		goto check_attribute_names2;
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_ci	t64 = le64_to_cpu(rst->open_attr_table_lsn);
425962306a36Sopenharmony_ci	err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb);
426062306a36Sopenharmony_ci	if (err)
426162306a36Sopenharmony_ci		goto out;
426262306a36Sopenharmony_ci
426362306a36Sopenharmony_ci	lrh = lcb->log_rec;
426462306a36Sopenharmony_ci	frh = lcb->lrh;
426562306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id),
426862306a36Sopenharmony_ci			   bytes_per_attr_entry)) {
426962306a36Sopenharmony_ci		err = -EINVAL;
427062306a36Sopenharmony_ci		goto out;
427162306a36Sopenharmony_ci	}
427262306a36Sopenharmony_ci
427362306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->redo_off);
427462306a36Sopenharmony_ci
427562306a36Sopenharmony_ci	rt = Add2Ptr(lrh, t16);
427662306a36Sopenharmony_ci	t32 = rec_len - t16;
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ci	if (!check_rstbl(rt, t32)) {
427962306a36Sopenharmony_ci		err = -EINVAL;
428062306a36Sopenharmony_ci		goto out;
428162306a36Sopenharmony_ci	}
428262306a36Sopenharmony_ci
428362306a36Sopenharmony_ci	oatbl = kmemdup(rt, t32, GFP_NOFS);
428462306a36Sopenharmony_ci	if (!oatbl) {
428562306a36Sopenharmony_ci		err = -ENOMEM;
428662306a36Sopenharmony_ci		goto out;
428762306a36Sopenharmony_ci	}
428862306a36Sopenharmony_ci
428962306a36Sopenharmony_ci	log->open_attr_tbl = oatbl;
429062306a36Sopenharmony_ci
429162306a36Sopenharmony_ci	/* Clear all of the Attr pointers. */
429262306a36Sopenharmony_ci	oe = NULL;
429362306a36Sopenharmony_ci	while ((oe = enum_rstbl(oatbl, oe))) {
429462306a36Sopenharmony_ci		if (!rst->major_ver) {
429562306a36Sopenharmony_ci			struct OPEN_ATTR_ENRTY_32 oe0;
429662306a36Sopenharmony_ci
429762306a36Sopenharmony_ci			/* Really 'oe' points to OPEN_ATTR_ENRTY_32. */
429862306a36Sopenharmony_ci			memcpy(&oe0, oe, SIZEOF_OPENATTRIBUTEENTRY0);
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_ci			oe->bytes_per_index = oe0.bytes_per_index;
430162306a36Sopenharmony_ci			oe->type = oe0.type;
430262306a36Sopenharmony_ci			oe->is_dirty_pages = oe0.is_dirty_pages;
430362306a36Sopenharmony_ci			oe->name_len = 0;
430462306a36Sopenharmony_ci			oe->ref = oe0.ref;
430562306a36Sopenharmony_ci			oe->open_record_lsn = oe0.open_record_lsn;
430662306a36Sopenharmony_ci		}
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci		oe->is_attr_name = 0;
430962306a36Sopenharmony_ci		oe->ptr = NULL;
431062306a36Sopenharmony_ci	}
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci	lcb_put(lcb);
431362306a36Sopenharmony_ci	lcb = NULL;
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_cicheck_attribute_names2:
431662306a36Sopenharmony_ci	if (rst->attr_names_len && oatbl) {
431762306a36Sopenharmony_ci		struct ATTR_NAME_ENTRY *ane = attr_names;
431862306a36Sopenharmony_ci		while (ane->off) {
431962306a36Sopenharmony_ci			/* TODO: Clear table on exit! */
432062306a36Sopenharmony_ci			oe = Add2Ptr(oatbl, le16_to_cpu(ane->off));
432162306a36Sopenharmony_ci			t16 = le16_to_cpu(ane->name_bytes);
432262306a36Sopenharmony_ci			oe->name_len = t16 / sizeof(short);
432362306a36Sopenharmony_ci			oe->ptr = ane->name;
432462306a36Sopenharmony_ci			oe->is_attr_name = 2;
432562306a36Sopenharmony_ci			ane = Add2Ptr(ane,
432662306a36Sopenharmony_ci				      sizeof(struct ATTR_NAME_ENTRY) + t16);
432762306a36Sopenharmony_ci		}
432862306a36Sopenharmony_ci	}
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	/*
433162306a36Sopenharmony_ci	 * If the checkpt_lsn is zero, then this is a freshly
433262306a36Sopenharmony_ci	 * formatted disk and we have no work to do.
433362306a36Sopenharmony_ci	 */
433462306a36Sopenharmony_ci	if (!checkpt_lsn) {
433562306a36Sopenharmony_ci		err = 0;
433662306a36Sopenharmony_ci		goto out;
433762306a36Sopenharmony_ci	}
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci	if (!oatbl) {
434062306a36Sopenharmony_ci		oatbl = init_rsttbl(bytes_per_attr_entry, 8);
434162306a36Sopenharmony_ci		if (!oatbl) {
434262306a36Sopenharmony_ci			err = -ENOMEM;
434362306a36Sopenharmony_ci			goto out;
434462306a36Sopenharmony_ci		}
434562306a36Sopenharmony_ci	}
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci	log->open_attr_tbl = oatbl;
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_ci	/* Start the analysis pass from the Checkpoint lsn. */
435062306a36Sopenharmony_ci	rec_lsn = checkpt_lsn;
435162306a36Sopenharmony_ci
435262306a36Sopenharmony_ci	/* Read the first lsn. */
435362306a36Sopenharmony_ci	err = read_log_rec_lcb(log, checkpt_lsn, lcb_ctx_next, &lcb);
435462306a36Sopenharmony_ci	if (err)
435562306a36Sopenharmony_ci		goto out;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	/* Loop to read all subsequent records to the end of the log file. */
435862306a36Sopenharmony_cinext_log_record_analyze:
435962306a36Sopenharmony_ci	err = read_next_log_rec(log, lcb, &rec_lsn);
436062306a36Sopenharmony_ci	if (err)
436162306a36Sopenharmony_ci		goto out;
436262306a36Sopenharmony_ci
436362306a36Sopenharmony_ci	if (!rec_lsn)
436462306a36Sopenharmony_ci		goto end_log_records_enumerate;
436562306a36Sopenharmony_ci
436662306a36Sopenharmony_ci	frh = lcb->lrh;
436762306a36Sopenharmony_ci	transact_id = le32_to_cpu(frh->transact_id);
436862306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
436962306a36Sopenharmony_ci	lrh = lcb->log_rec;
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, transact_id, bytes_per_attr_entry)) {
437262306a36Sopenharmony_ci		err = -EINVAL;
437362306a36Sopenharmony_ci		goto out;
437462306a36Sopenharmony_ci	}
437562306a36Sopenharmony_ci
437662306a36Sopenharmony_ci	/*
437762306a36Sopenharmony_ci	 * The first lsn after the previous lsn remembered
437862306a36Sopenharmony_ci	 * the checkpoint is the first candidate for the rlsn.
437962306a36Sopenharmony_ci	 */
438062306a36Sopenharmony_ci	if (!rlsn)
438162306a36Sopenharmony_ci		rlsn = rec_lsn;
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci	if (LfsClientRecord != frh->record_type)
438462306a36Sopenharmony_ci		goto next_log_record_analyze;
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci	/*
438762306a36Sopenharmony_ci	 * Now update the Transaction Table for this transaction. If there
438862306a36Sopenharmony_ci	 * is no entry present or it is unallocated we allocate the entry.
438962306a36Sopenharmony_ci	 */
439062306a36Sopenharmony_ci	if (!trtbl) {
439162306a36Sopenharmony_ci		trtbl = init_rsttbl(sizeof(struct TRANSACTION_ENTRY),
439262306a36Sopenharmony_ci				    INITIAL_NUMBER_TRANSACTIONS);
439362306a36Sopenharmony_ci		if (!trtbl) {
439462306a36Sopenharmony_ci			err = -ENOMEM;
439562306a36Sopenharmony_ci			goto out;
439662306a36Sopenharmony_ci		}
439762306a36Sopenharmony_ci	}
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci	tr = Add2Ptr(trtbl, transact_id);
440062306a36Sopenharmony_ci
440162306a36Sopenharmony_ci	if (transact_id >= bytes_per_rt(trtbl) ||
440262306a36Sopenharmony_ci	    tr->next != RESTART_ENTRY_ALLOCATED_LE) {
440362306a36Sopenharmony_ci		tr = alloc_rsttbl_from_idx(&trtbl, transact_id);
440462306a36Sopenharmony_ci		if (!tr) {
440562306a36Sopenharmony_ci			err = -ENOMEM;
440662306a36Sopenharmony_ci			goto out;
440762306a36Sopenharmony_ci		}
440862306a36Sopenharmony_ci		tr->transact_state = TransactionActive;
440962306a36Sopenharmony_ci		tr->first_lsn = cpu_to_le64(rec_lsn);
441062306a36Sopenharmony_ci	}
441162306a36Sopenharmony_ci
441262306a36Sopenharmony_ci	tr->prev_lsn = tr->undo_next_lsn = cpu_to_le64(rec_lsn);
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_ci	/*
441562306a36Sopenharmony_ci	 * If this is a compensation log record, then change
441662306a36Sopenharmony_ci	 * the undo_next_lsn to be the undo_next_lsn of this record.
441762306a36Sopenharmony_ci	 */
441862306a36Sopenharmony_ci	if (lrh->undo_op == cpu_to_le16(CompensationLogRecord))
441962306a36Sopenharmony_ci		tr->undo_next_lsn = frh->client_undo_next_lsn;
442062306a36Sopenharmony_ci
442162306a36Sopenharmony_ci	/* Dispatch to handle log record depending on type. */
442262306a36Sopenharmony_ci	switch (le16_to_cpu(lrh->redo_op)) {
442362306a36Sopenharmony_ci	case InitializeFileRecordSegment:
442462306a36Sopenharmony_ci	case DeallocateFileRecordSegment:
442562306a36Sopenharmony_ci	case WriteEndOfFileRecordSegment:
442662306a36Sopenharmony_ci	case CreateAttribute:
442762306a36Sopenharmony_ci	case DeleteAttribute:
442862306a36Sopenharmony_ci	case UpdateResidentValue:
442962306a36Sopenharmony_ci	case UpdateNonresidentValue:
443062306a36Sopenharmony_ci	case UpdateMappingPairs:
443162306a36Sopenharmony_ci	case SetNewAttributeSizes:
443262306a36Sopenharmony_ci	case AddIndexEntryRoot:
443362306a36Sopenharmony_ci	case DeleteIndexEntryRoot:
443462306a36Sopenharmony_ci	case AddIndexEntryAllocation:
443562306a36Sopenharmony_ci	case DeleteIndexEntryAllocation:
443662306a36Sopenharmony_ci	case WriteEndOfIndexBuffer:
443762306a36Sopenharmony_ci	case SetIndexEntryVcnRoot:
443862306a36Sopenharmony_ci	case SetIndexEntryVcnAllocation:
443962306a36Sopenharmony_ci	case UpdateFileNameRoot:
444062306a36Sopenharmony_ci	case UpdateFileNameAllocation:
444162306a36Sopenharmony_ci	case SetBitsInNonresidentBitMap:
444262306a36Sopenharmony_ci	case ClearBitsInNonresidentBitMap:
444362306a36Sopenharmony_ci	case UpdateRecordDataRoot:
444462306a36Sopenharmony_ci	case UpdateRecordDataAllocation:
444562306a36Sopenharmony_ci	case ZeroEndOfFileRecord:
444662306a36Sopenharmony_ci		t16 = le16_to_cpu(lrh->target_attr);
444762306a36Sopenharmony_ci		t64 = le64_to_cpu(lrh->target_vcn);
444862306a36Sopenharmony_ci		dp = find_dp(dptbl, t16, t64);
444962306a36Sopenharmony_ci
445062306a36Sopenharmony_ci		if (dp)
445162306a36Sopenharmony_ci			goto copy_lcns;
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci		/*
445462306a36Sopenharmony_ci		 * Calculate the number of clusters per page the system
445562306a36Sopenharmony_ci		 * which wrote the checkpoint, possibly creating the table.
445662306a36Sopenharmony_ci		 */
445762306a36Sopenharmony_ci		if (dptbl) {
445862306a36Sopenharmony_ci			t32 = (le16_to_cpu(dptbl->size) -
445962306a36Sopenharmony_ci			       sizeof(struct DIR_PAGE_ENTRY)) /
446062306a36Sopenharmony_ci			      sizeof(u64);
446162306a36Sopenharmony_ci		} else {
446262306a36Sopenharmony_ci			t32 = log->clst_per_page;
446362306a36Sopenharmony_ci			kfree(dptbl);
446462306a36Sopenharmony_ci			dptbl = init_rsttbl(struct_size(dp, page_lcns, t32),
446562306a36Sopenharmony_ci					    32);
446662306a36Sopenharmony_ci			if (!dptbl) {
446762306a36Sopenharmony_ci				err = -ENOMEM;
446862306a36Sopenharmony_ci				goto out;
446962306a36Sopenharmony_ci			}
447062306a36Sopenharmony_ci		}
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_ci		dp = alloc_rsttbl_idx(&dptbl);
447362306a36Sopenharmony_ci		if (!dp) {
447462306a36Sopenharmony_ci			err = -ENOMEM;
447562306a36Sopenharmony_ci			goto out;
447662306a36Sopenharmony_ci		}
447762306a36Sopenharmony_ci		dp->target_attr = cpu_to_le32(t16);
447862306a36Sopenharmony_ci		dp->transfer_len = cpu_to_le32(t32 << sbi->cluster_bits);
447962306a36Sopenharmony_ci		dp->lcns_follow = cpu_to_le32(t32);
448062306a36Sopenharmony_ci		dp->vcn = cpu_to_le64(t64 & ~((u64)t32 - 1));
448162306a36Sopenharmony_ci		dp->oldest_lsn = cpu_to_le64(rec_lsn);
448262306a36Sopenharmony_ci
448362306a36Sopenharmony_cicopy_lcns:
448462306a36Sopenharmony_ci		/*
448562306a36Sopenharmony_ci		 * Copy the Lcns from the log record into the Dirty Page Entry.
448662306a36Sopenharmony_ci		 * TODO: For different page size support, must somehow make
448762306a36Sopenharmony_ci		 * whole routine a loop, case Lcns do not fit below.
448862306a36Sopenharmony_ci		 */
448962306a36Sopenharmony_ci		t16 = le16_to_cpu(lrh->lcns_follow);
449062306a36Sopenharmony_ci		for (i = 0; i < t16; i++) {
449162306a36Sopenharmony_ci			size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
449262306a36Sopenharmony_ci					    le64_to_cpu(dp->vcn));
449362306a36Sopenharmony_ci			dp->page_lcns[j + i] = lrh->page_lcns[i];
449462306a36Sopenharmony_ci		}
449562306a36Sopenharmony_ci
449662306a36Sopenharmony_ci		goto next_log_record_analyze;
449762306a36Sopenharmony_ci
449862306a36Sopenharmony_ci	case DeleteDirtyClusters: {
449962306a36Sopenharmony_ci		u32 range_count =
450062306a36Sopenharmony_ci			le16_to_cpu(lrh->redo_len) / sizeof(struct LCN_RANGE);
450162306a36Sopenharmony_ci		const struct LCN_RANGE *r =
450262306a36Sopenharmony_ci			Add2Ptr(lrh, le16_to_cpu(lrh->redo_off));
450362306a36Sopenharmony_ci
450462306a36Sopenharmony_ci		/* Loop through all of the Lcn ranges this log record. */
450562306a36Sopenharmony_ci		for (i = 0; i < range_count; i++, r++) {
450662306a36Sopenharmony_ci			u64 lcn0 = le64_to_cpu(r->lcn);
450762306a36Sopenharmony_ci			u64 lcn_e = lcn0 + le64_to_cpu(r->len) - 1;
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci			dp = NULL;
451062306a36Sopenharmony_ci			while ((dp = enum_rstbl(dptbl, dp))) {
451162306a36Sopenharmony_ci				u32 j;
451262306a36Sopenharmony_ci
451362306a36Sopenharmony_ci				t32 = le32_to_cpu(dp->lcns_follow);
451462306a36Sopenharmony_ci				for (j = 0; j < t32; j++) {
451562306a36Sopenharmony_ci					t64 = le64_to_cpu(dp->page_lcns[j]);
451662306a36Sopenharmony_ci					if (t64 >= lcn0 && t64 <= lcn_e)
451762306a36Sopenharmony_ci						dp->page_lcns[j] = 0;
451862306a36Sopenharmony_ci				}
451962306a36Sopenharmony_ci			}
452062306a36Sopenharmony_ci		}
452162306a36Sopenharmony_ci		goto next_log_record_analyze;
452262306a36Sopenharmony_ci		;
452362306a36Sopenharmony_ci	}
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci	case OpenNonresidentAttribute:
452662306a36Sopenharmony_ci		t16 = le16_to_cpu(lrh->target_attr);
452762306a36Sopenharmony_ci		if (t16 >= bytes_per_rt(oatbl)) {
452862306a36Sopenharmony_ci			/*
452962306a36Sopenharmony_ci			 * Compute how big the table needs to be.
453062306a36Sopenharmony_ci			 * Add 10 extra entries for some cushion.
453162306a36Sopenharmony_ci			 */
453262306a36Sopenharmony_ci			u32 new_e = t16 / le16_to_cpu(oatbl->size);
453362306a36Sopenharmony_ci
453462306a36Sopenharmony_ci			new_e += 10 - le16_to_cpu(oatbl->used);
453562306a36Sopenharmony_ci
453662306a36Sopenharmony_ci			oatbl = extend_rsttbl(oatbl, new_e, ~0u);
453762306a36Sopenharmony_ci			log->open_attr_tbl = oatbl;
453862306a36Sopenharmony_ci			if (!oatbl) {
453962306a36Sopenharmony_ci				err = -ENOMEM;
454062306a36Sopenharmony_ci				goto out;
454162306a36Sopenharmony_ci			}
454262306a36Sopenharmony_ci		}
454362306a36Sopenharmony_ci
454462306a36Sopenharmony_ci		/* Point to the entry being opened. */
454562306a36Sopenharmony_ci		oe = alloc_rsttbl_from_idx(&oatbl, t16);
454662306a36Sopenharmony_ci		log->open_attr_tbl = oatbl;
454762306a36Sopenharmony_ci		if (!oe) {
454862306a36Sopenharmony_ci			err = -ENOMEM;
454962306a36Sopenharmony_ci			goto out;
455062306a36Sopenharmony_ci		}
455162306a36Sopenharmony_ci
455262306a36Sopenharmony_ci		/* Initialize this entry from the log record. */
455362306a36Sopenharmony_ci		t16 = le16_to_cpu(lrh->redo_off);
455462306a36Sopenharmony_ci		if (!rst->major_ver) {
455562306a36Sopenharmony_ci			/* Convert version '0' into version '1'. */
455662306a36Sopenharmony_ci			struct OPEN_ATTR_ENRTY_32 *oe0 = Add2Ptr(lrh, t16);
455762306a36Sopenharmony_ci
455862306a36Sopenharmony_ci			oe->bytes_per_index = oe0->bytes_per_index;
455962306a36Sopenharmony_ci			oe->type = oe0->type;
456062306a36Sopenharmony_ci			oe->is_dirty_pages = oe0->is_dirty_pages;
456162306a36Sopenharmony_ci			oe->name_len = 0; //oe0.name_len;
456262306a36Sopenharmony_ci			oe->ref = oe0->ref;
456362306a36Sopenharmony_ci			oe->open_record_lsn = oe0->open_record_lsn;
456462306a36Sopenharmony_ci		} else {
456562306a36Sopenharmony_ci			memcpy(oe, Add2Ptr(lrh, t16), bytes_per_attr_entry);
456662306a36Sopenharmony_ci		}
456762306a36Sopenharmony_ci
456862306a36Sopenharmony_ci		t16 = le16_to_cpu(lrh->undo_len);
456962306a36Sopenharmony_ci		if (t16) {
457062306a36Sopenharmony_ci			oe->ptr = kmalloc(t16, GFP_NOFS);
457162306a36Sopenharmony_ci			if (!oe->ptr) {
457262306a36Sopenharmony_ci				err = -ENOMEM;
457362306a36Sopenharmony_ci				goto out;
457462306a36Sopenharmony_ci			}
457562306a36Sopenharmony_ci			oe->name_len = t16 / sizeof(short);
457662306a36Sopenharmony_ci			memcpy(oe->ptr,
457762306a36Sopenharmony_ci			       Add2Ptr(lrh, le16_to_cpu(lrh->undo_off)), t16);
457862306a36Sopenharmony_ci			oe->is_attr_name = 1;
457962306a36Sopenharmony_ci		} else {
458062306a36Sopenharmony_ci			oe->ptr = NULL;
458162306a36Sopenharmony_ci			oe->is_attr_name = 0;
458262306a36Sopenharmony_ci		}
458362306a36Sopenharmony_ci
458462306a36Sopenharmony_ci		goto next_log_record_analyze;
458562306a36Sopenharmony_ci
458662306a36Sopenharmony_ci	case HotFix:
458762306a36Sopenharmony_ci		t16 = le16_to_cpu(lrh->target_attr);
458862306a36Sopenharmony_ci		t64 = le64_to_cpu(lrh->target_vcn);
458962306a36Sopenharmony_ci		dp = find_dp(dptbl, t16, t64);
459062306a36Sopenharmony_ci		if (dp) {
459162306a36Sopenharmony_ci			size_t j = le64_to_cpu(lrh->target_vcn) -
459262306a36Sopenharmony_ci				   le64_to_cpu(dp->vcn);
459362306a36Sopenharmony_ci			if (dp->page_lcns[j])
459462306a36Sopenharmony_ci				dp->page_lcns[j] = lrh->page_lcns[0];
459562306a36Sopenharmony_ci		}
459662306a36Sopenharmony_ci		goto next_log_record_analyze;
459762306a36Sopenharmony_ci
459862306a36Sopenharmony_ci	case EndTopLevelAction:
459962306a36Sopenharmony_ci		tr = Add2Ptr(trtbl, transact_id);
460062306a36Sopenharmony_ci		tr->prev_lsn = cpu_to_le64(rec_lsn);
460162306a36Sopenharmony_ci		tr->undo_next_lsn = frh->client_undo_next_lsn;
460262306a36Sopenharmony_ci		goto next_log_record_analyze;
460362306a36Sopenharmony_ci
460462306a36Sopenharmony_ci	case PrepareTransaction:
460562306a36Sopenharmony_ci		tr = Add2Ptr(trtbl, transact_id);
460662306a36Sopenharmony_ci		tr->transact_state = TransactionPrepared;
460762306a36Sopenharmony_ci		goto next_log_record_analyze;
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_ci	case CommitTransaction:
461062306a36Sopenharmony_ci		tr = Add2Ptr(trtbl, transact_id);
461162306a36Sopenharmony_ci		tr->transact_state = TransactionCommitted;
461262306a36Sopenharmony_ci		goto next_log_record_analyze;
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	case ForgetTransaction:
461562306a36Sopenharmony_ci		free_rsttbl_idx(trtbl, transact_id);
461662306a36Sopenharmony_ci		goto next_log_record_analyze;
461762306a36Sopenharmony_ci
461862306a36Sopenharmony_ci	case Noop:
461962306a36Sopenharmony_ci	case OpenAttributeTableDump:
462062306a36Sopenharmony_ci	case AttributeNamesDump:
462162306a36Sopenharmony_ci	case DirtyPageTableDump:
462262306a36Sopenharmony_ci	case TransactionTableDump:
462362306a36Sopenharmony_ci		/* The following cases require no action the Analysis Pass. */
462462306a36Sopenharmony_ci		goto next_log_record_analyze;
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci	default:
462762306a36Sopenharmony_ci		/*
462862306a36Sopenharmony_ci		 * All codes will be explicitly handled.
462962306a36Sopenharmony_ci		 * If we see a code we do not expect, then we are trouble.
463062306a36Sopenharmony_ci		 */
463162306a36Sopenharmony_ci		goto next_log_record_analyze;
463262306a36Sopenharmony_ci	}
463362306a36Sopenharmony_ci
463462306a36Sopenharmony_ciend_log_records_enumerate:
463562306a36Sopenharmony_ci	lcb_put(lcb);
463662306a36Sopenharmony_ci	lcb = NULL;
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci	/*
463962306a36Sopenharmony_ci	 * Scan the Dirty Page Table and Transaction Table for
464062306a36Sopenharmony_ci	 * the lowest lsn, and return it as the Redo lsn.
464162306a36Sopenharmony_ci	 */
464262306a36Sopenharmony_ci	dp = NULL;
464362306a36Sopenharmony_ci	while ((dp = enum_rstbl(dptbl, dp))) {
464462306a36Sopenharmony_ci		t64 = le64_to_cpu(dp->oldest_lsn);
464562306a36Sopenharmony_ci		if (t64 && t64 < rlsn)
464662306a36Sopenharmony_ci			rlsn = t64;
464762306a36Sopenharmony_ci	}
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_ci	tr = NULL;
465062306a36Sopenharmony_ci	while ((tr = enum_rstbl(trtbl, tr))) {
465162306a36Sopenharmony_ci		t64 = le64_to_cpu(tr->first_lsn);
465262306a36Sopenharmony_ci		if (t64 && t64 < rlsn)
465362306a36Sopenharmony_ci			rlsn = t64;
465462306a36Sopenharmony_ci	}
465562306a36Sopenharmony_ci
465662306a36Sopenharmony_ci	/*
465762306a36Sopenharmony_ci	 * Only proceed if the Dirty Page Table or Transaction
465862306a36Sopenharmony_ci	 * table are not empty.
465962306a36Sopenharmony_ci	 */
466062306a36Sopenharmony_ci	if ((!dptbl || !dptbl->total) && (!trtbl || !trtbl->total))
466162306a36Sopenharmony_ci		goto end_reply;
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_ci	sbi->flags |= NTFS_FLAGS_NEED_REPLAY;
466462306a36Sopenharmony_ci	if (is_ro)
466562306a36Sopenharmony_ci		goto out;
466662306a36Sopenharmony_ci
466762306a36Sopenharmony_ci	/* Reopen all of the attributes with dirty pages. */
466862306a36Sopenharmony_ci	oe = NULL;
466962306a36Sopenharmony_cinext_open_attribute:
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	oe = enum_rstbl(oatbl, oe);
467262306a36Sopenharmony_ci	if (!oe) {
467362306a36Sopenharmony_ci		err = 0;
467462306a36Sopenharmony_ci		dp = NULL;
467562306a36Sopenharmony_ci		goto next_dirty_page;
467662306a36Sopenharmony_ci	}
467762306a36Sopenharmony_ci
467862306a36Sopenharmony_ci	oa = kzalloc(sizeof(struct OpenAttr), GFP_NOFS);
467962306a36Sopenharmony_ci	if (!oa) {
468062306a36Sopenharmony_ci		err = -ENOMEM;
468162306a36Sopenharmony_ci		goto out;
468262306a36Sopenharmony_ci	}
468362306a36Sopenharmony_ci
468462306a36Sopenharmony_ci	inode = ntfs_iget5(sbi->sb, &oe->ref, NULL);
468562306a36Sopenharmony_ci	if (IS_ERR(inode))
468662306a36Sopenharmony_ci		goto fake_attr;
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_ci	if (is_bad_inode(inode)) {
468962306a36Sopenharmony_ci		iput(inode);
469062306a36Sopenharmony_cifake_attr:
469162306a36Sopenharmony_ci		if (oa->ni) {
469262306a36Sopenharmony_ci			iput(&oa->ni->vfs_inode);
469362306a36Sopenharmony_ci			oa->ni = NULL;
469462306a36Sopenharmony_ci		}
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci		attr = attr_create_nonres_log(sbi, oe->type, 0, oe->ptr,
469762306a36Sopenharmony_ci					      oe->name_len, 0);
469862306a36Sopenharmony_ci		if (!attr) {
469962306a36Sopenharmony_ci			kfree(oa);
470062306a36Sopenharmony_ci			err = -ENOMEM;
470162306a36Sopenharmony_ci			goto out;
470262306a36Sopenharmony_ci		}
470362306a36Sopenharmony_ci		oa->attr = attr;
470462306a36Sopenharmony_ci		oa->run1 = &oa->run0;
470562306a36Sopenharmony_ci		goto final_oe;
470662306a36Sopenharmony_ci	}
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_ci	ni_oe = ntfs_i(inode);
470962306a36Sopenharmony_ci	oa->ni = ni_oe;
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_ci	attr = ni_find_attr(ni_oe, NULL, NULL, oe->type, oe->ptr, oe->name_len,
471262306a36Sopenharmony_ci			    NULL, NULL);
471362306a36Sopenharmony_ci
471462306a36Sopenharmony_ci	if (!attr)
471562306a36Sopenharmony_ci		goto fake_attr;
471662306a36Sopenharmony_ci
471762306a36Sopenharmony_ci	t32 = le32_to_cpu(attr->size);
471862306a36Sopenharmony_ci	oa->attr = kmemdup(attr, t32, GFP_NOFS);
471962306a36Sopenharmony_ci	if (!oa->attr)
472062306a36Sopenharmony_ci		goto fake_attr;
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_ci	if (!S_ISDIR(inode->i_mode)) {
472362306a36Sopenharmony_ci		if (attr->type == ATTR_DATA && !attr->name_len) {
472462306a36Sopenharmony_ci			oa->run1 = &ni_oe->file.run;
472562306a36Sopenharmony_ci			goto final_oe;
472662306a36Sopenharmony_ci		}
472762306a36Sopenharmony_ci	} else {
472862306a36Sopenharmony_ci		if (attr->type == ATTR_ALLOC &&
472962306a36Sopenharmony_ci		    attr->name_len == ARRAY_SIZE(I30_NAME) &&
473062306a36Sopenharmony_ci		    !memcmp(attr_name(attr), I30_NAME, sizeof(I30_NAME))) {
473162306a36Sopenharmony_ci			oa->run1 = &ni_oe->dir.alloc_run;
473262306a36Sopenharmony_ci			goto final_oe;
473362306a36Sopenharmony_ci		}
473462306a36Sopenharmony_ci	}
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_ci	if (attr->non_res) {
473762306a36Sopenharmony_ci		u16 roff = le16_to_cpu(attr->nres.run_off);
473862306a36Sopenharmony_ci		CLST svcn = le64_to_cpu(attr->nres.svcn);
473962306a36Sopenharmony_ci
474062306a36Sopenharmony_ci		if (roff > t32) {
474162306a36Sopenharmony_ci			kfree(oa->attr);
474262306a36Sopenharmony_ci			oa->attr = NULL;
474362306a36Sopenharmony_ci			goto fake_attr;
474462306a36Sopenharmony_ci		}
474562306a36Sopenharmony_ci
474662306a36Sopenharmony_ci		err = run_unpack(&oa->run0, sbi, inode->i_ino, svcn,
474762306a36Sopenharmony_ci				 le64_to_cpu(attr->nres.evcn), svcn,
474862306a36Sopenharmony_ci				 Add2Ptr(attr, roff), t32 - roff);
474962306a36Sopenharmony_ci		if (err < 0) {
475062306a36Sopenharmony_ci			kfree(oa->attr);
475162306a36Sopenharmony_ci			oa->attr = NULL;
475262306a36Sopenharmony_ci			goto fake_attr;
475362306a36Sopenharmony_ci		}
475462306a36Sopenharmony_ci		err = 0;
475562306a36Sopenharmony_ci	}
475662306a36Sopenharmony_ci	oa->run1 = &oa->run0;
475762306a36Sopenharmony_ci	attr = oa->attr;
475862306a36Sopenharmony_ci
475962306a36Sopenharmony_cifinal_oe:
476062306a36Sopenharmony_ci	if (oe->is_attr_name == 1)
476162306a36Sopenharmony_ci		kfree(oe->ptr);
476262306a36Sopenharmony_ci	oe->is_attr_name = 0;
476362306a36Sopenharmony_ci	oe->ptr = oa;
476462306a36Sopenharmony_ci	oe->name_len = attr->name_len;
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_ci	goto next_open_attribute;
476762306a36Sopenharmony_ci
476862306a36Sopenharmony_ci	/*
476962306a36Sopenharmony_ci	 * Now loop through the dirty page table to extract all of the Vcn/Lcn.
477062306a36Sopenharmony_ci	 * Mapping that we have, and insert it into the appropriate run.
477162306a36Sopenharmony_ci	 */
477262306a36Sopenharmony_cinext_dirty_page:
477362306a36Sopenharmony_ci	dp = enum_rstbl(dptbl, dp);
477462306a36Sopenharmony_ci	if (!dp)
477562306a36Sopenharmony_ci		goto do_redo_1;
477662306a36Sopenharmony_ci
477762306a36Sopenharmony_ci	oe = Add2Ptr(oatbl, le32_to_cpu(dp->target_attr));
477862306a36Sopenharmony_ci
477962306a36Sopenharmony_ci	if (oe->next != RESTART_ENTRY_ALLOCATED_LE)
478062306a36Sopenharmony_ci		goto next_dirty_page;
478162306a36Sopenharmony_ci
478262306a36Sopenharmony_ci	oa = oe->ptr;
478362306a36Sopenharmony_ci	if (!oa)
478462306a36Sopenharmony_ci		goto next_dirty_page;
478562306a36Sopenharmony_ci
478662306a36Sopenharmony_ci	i = -1;
478762306a36Sopenharmony_cinext_dirty_page_vcn:
478862306a36Sopenharmony_ci	i += 1;
478962306a36Sopenharmony_ci	if (i >= le32_to_cpu(dp->lcns_follow))
479062306a36Sopenharmony_ci		goto next_dirty_page;
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	vcn = le64_to_cpu(dp->vcn) + i;
479362306a36Sopenharmony_ci	size = (vcn + 1) << sbi->cluster_bits;
479462306a36Sopenharmony_ci
479562306a36Sopenharmony_ci	if (!dp->page_lcns[i])
479662306a36Sopenharmony_ci		goto next_dirty_page_vcn;
479762306a36Sopenharmony_ci
479862306a36Sopenharmony_ci	rno = ino_get(&oe->ref);
479962306a36Sopenharmony_ci	if (rno <= MFT_REC_MIRR &&
480062306a36Sopenharmony_ci	    size < (MFT_REC_VOL + 1) * sbi->record_size &&
480162306a36Sopenharmony_ci	    oe->type == ATTR_DATA) {
480262306a36Sopenharmony_ci		goto next_dirty_page_vcn;
480362306a36Sopenharmony_ci	}
480462306a36Sopenharmony_ci
480562306a36Sopenharmony_ci	lcn = le64_to_cpu(dp->page_lcns[i]);
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci	if ((!run_lookup_entry(oa->run1, vcn, &lcn0, &len0, NULL) ||
480862306a36Sopenharmony_ci	     lcn0 != lcn) &&
480962306a36Sopenharmony_ci	    !run_add_entry(oa->run1, vcn, lcn, 1, false)) {
481062306a36Sopenharmony_ci		err = -ENOMEM;
481162306a36Sopenharmony_ci		goto out;
481262306a36Sopenharmony_ci	}
481362306a36Sopenharmony_ci	attr = oa->attr;
481462306a36Sopenharmony_ci	if (size > le64_to_cpu(attr->nres.alloc_size)) {
481562306a36Sopenharmony_ci		attr->nres.valid_size = attr->nres.data_size =
481662306a36Sopenharmony_ci			attr->nres.alloc_size = cpu_to_le64(size);
481762306a36Sopenharmony_ci	}
481862306a36Sopenharmony_ci	goto next_dirty_page_vcn;
481962306a36Sopenharmony_ci
482062306a36Sopenharmony_cido_redo_1:
482162306a36Sopenharmony_ci	/*
482262306a36Sopenharmony_ci	 * Perform the Redo Pass, to restore all of the dirty pages to the same
482362306a36Sopenharmony_ci	 * contents that they had immediately before the crash. If the dirty
482462306a36Sopenharmony_ci	 * page table is empty, then we can skip the entire Redo Pass.
482562306a36Sopenharmony_ci	 */
482662306a36Sopenharmony_ci	if (!dptbl || !dptbl->total)
482762306a36Sopenharmony_ci		goto do_undo_action;
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci	rec_lsn = rlsn;
483062306a36Sopenharmony_ci
483162306a36Sopenharmony_ci	/*
483262306a36Sopenharmony_ci	 * Read the record at the Redo lsn, before falling
483362306a36Sopenharmony_ci	 * into common code to handle each record.
483462306a36Sopenharmony_ci	 */
483562306a36Sopenharmony_ci	err = read_log_rec_lcb(log, rlsn, lcb_ctx_next, &lcb);
483662306a36Sopenharmony_ci	if (err)
483762306a36Sopenharmony_ci		goto out;
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_ci	/*
484062306a36Sopenharmony_ci	 * Now loop to read all of our log records forwards, until
484162306a36Sopenharmony_ci	 * we hit the end of the file, cleaning up at the end.
484262306a36Sopenharmony_ci	 */
484362306a36Sopenharmony_cido_action_next:
484462306a36Sopenharmony_ci	frh = lcb->lrh;
484562306a36Sopenharmony_ci
484662306a36Sopenharmony_ci	if (LfsClientRecord != frh->record_type)
484762306a36Sopenharmony_ci		goto read_next_log_do_action;
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci	transact_id = le32_to_cpu(frh->transact_id);
485062306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
485162306a36Sopenharmony_ci	lrh = lcb->log_rec;
485262306a36Sopenharmony_ci
485362306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, transact_id, bytes_per_attr_entry)) {
485462306a36Sopenharmony_ci		err = -EINVAL;
485562306a36Sopenharmony_ci		goto out;
485662306a36Sopenharmony_ci	}
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_ci	/* Ignore log records that do not update pages. */
485962306a36Sopenharmony_ci	if (lrh->lcns_follow)
486062306a36Sopenharmony_ci		goto find_dirty_page;
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci	goto read_next_log_do_action;
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_cifind_dirty_page:
486562306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->target_attr);
486662306a36Sopenharmony_ci	t64 = le64_to_cpu(lrh->target_vcn);
486762306a36Sopenharmony_ci	dp = find_dp(dptbl, t16, t64);
486862306a36Sopenharmony_ci
486962306a36Sopenharmony_ci	if (!dp)
487062306a36Sopenharmony_ci		goto read_next_log_do_action;
487162306a36Sopenharmony_ci
487262306a36Sopenharmony_ci	if (rec_lsn < le64_to_cpu(dp->oldest_lsn))
487362306a36Sopenharmony_ci		goto read_next_log_do_action;
487462306a36Sopenharmony_ci
487562306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->target_attr);
487662306a36Sopenharmony_ci	if (t16 >= bytes_per_rt(oatbl)) {
487762306a36Sopenharmony_ci		err = -EINVAL;
487862306a36Sopenharmony_ci		goto out;
487962306a36Sopenharmony_ci	}
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_ci	oe = Add2Ptr(oatbl, t16);
488262306a36Sopenharmony_ci
488362306a36Sopenharmony_ci	if (oe->next != RESTART_ENTRY_ALLOCATED_LE) {
488462306a36Sopenharmony_ci		err = -EINVAL;
488562306a36Sopenharmony_ci		goto out;
488662306a36Sopenharmony_ci	}
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_ci	oa = oe->ptr;
488962306a36Sopenharmony_ci
489062306a36Sopenharmony_ci	if (!oa) {
489162306a36Sopenharmony_ci		err = -EINVAL;
489262306a36Sopenharmony_ci		goto out;
489362306a36Sopenharmony_ci	}
489462306a36Sopenharmony_ci	attr = oa->attr;
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci	vcn = le64_to_cpu(lrh->target_vcn);
489762306a36Sopenharmony_ci
489862306a36Sopenharmony_ci	if (!run_lookup_entry(oa->run1, vcn, &lcn, NULL, NULL) ||
489962306a36Sopenharmony_ci	    lcn == SPARSE_LCN) {
490062306a36Sopenharmony_ci		goto read_next_log_do_action;
490162306a36Sopenharmony_ci	}
490262306a36Sopenharmony_ci
490362306a36Sopenharmony_ci	/* Point to the Redo data and get its length. */
490462306a36Sopenharmony_ci	data = Add2Ptr(lrh, le16_to_cpu(lrh->redo_off));
490562306a36Sopenharmony_ci	dlen = le16_to_cpu(lrh->redo_len);
490662306a36Sopenharmony_ci
490762306a36Sopenharmony_ci	/* Shorten length by any Lcns which were deleted. */
490862306a36Sopenharmony_ci	saved_len = dlen;
490962306a36Sopenharmony_ci
491062306a36Sopenharmony_ci	for (i = le16_to_cpu(lrh->lcns_follow); i; i--) {
491162306a36Sopenharmony_ci		size_t j;
491262306a36Sopenharmony_ci		u32 alen, voff;
491362306a36Sopenharmony_ci
491462306a36Sopenharmony_ci		voff = le16_to_cpu(lrh->record_off) +
491562306a36Sopenharmony_ci		       le16_to_cpu(lrh->attr_off);
491662306a36Sopenharmony_ci		voff += le16_to_cpu(lrh->cluster_off) << SECTOR_SHIFT;
491762306a36Sopenharmony_ci
491862306a36Sopenharmony_ci		/* If the Vcn question is allocated, we can just get out. */
491962306a36Sopenharmony_ci		j = le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn);
492062306a36Sopenharmony_ci		if (dp->page_lcns[j + i - 1])
492162306a36Sopenharmony_ci			break;
492262306a36Sopenharmony_ci
492362306a36Sopenharmony_ci		if (!saved_len)
492462306a36Sopenharmony_ci			saved_len = 1;
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci		/*
492762306a36Sopenharmony_ci		 * Calculate the allocated space left relative to the
492862306a36Sopenharmony_ci		 * log record Vcn, after removing this unallocated Vcn.
492962306a36Sopenharmony_ci		 */
493062306a36Sopenharmony_ci		alen = (i - 1) << sbi->cluster_bits;
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci		/*
493362306a36Sopenharmony_ci		 * If the update described this log record goes beyond
493462306a36Sopenharmony_ci		 * the allocated space, then we will have to reduce the length.
493562306a36Sopenharmony_ci		 */
493662306a36Sopenharmony_ci		if (voff >= alen)
493762306a36Sopenharmony_ci			dlen = 0;
493862306a36Sopenharmony_ci		else if (voff + dlen > alen)
493962306a36Sopenharmony_ci			dlen = alen - voff;
494062306a36Sopenharmony_ci	}
494162306a36Sopenharmony_ci
494262306a36Sopenharmony_ci	/*
494362306a36Sopenharmony_ci	 * If the resulting dlen from above is now zero,
494462306a36Sopenharmony_ci	 * we can skip this log record.
494562306a36Sopenharmony_ci	 */
494662306a36Sopenharmony_ci	if (!dlen && saved_len)
494762306a36Sopenharmony_ci		goto read_next_log_do_action;
494862306a36Sopenharmony_ci
494962306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->redo_op);
495062306a36Sopenharmony_ci	if (can_skip_action(t16))
495162306a36Sopenharmony_ci		goto read_next_log_do_action;
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci	/* Apply the Redo operation a common routine. */
495462306a36Sopenharmony_ci	err = do_action(log, oe, lrh, t16, data, dlen, rec_len, &rec_lsn);
495562306a36Sopenharmony_ci	if (err)
495662306a36Sopenharmony_ci		goto out;
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci	/* Keep reading and looping back until end of file. */
495962306a36Sopenharmony_ciread_next_log_do_action:
496062306a36Sopenharmony_ci	err = read_next_log_rec(log, lcb, &rec_lsn);
496162306a36Sopenharmony_ci	if (!err && rec_lsn)
496262306a36Sopenharmony_ci		goto do_action_next;
496362306a36Sopenharmony_ci
496462306a36Sopenharmony_ci	lcb_put(lcb);
496562306a36Sopenharmony_ci	lcb = NULL;
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_cido_undo_action:
496862306a36Sopenharmony_ci	/* Scan Transaction Table. */
496962306a36Sopenharmony_ci	tr = NULL;
497062306a36Sopenharmony_citransaction_table_next:
497162306a36Sopenharmony_ci	tr = enum_rstbl(trtbl, tr);
497262306a36Sopenharmony_ci	if (!tr)
497362306a36Sopenharmony_ci		goto undo_action_done;
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci	if (TransactionActive != tr->transact_state || !tr->undo_next_lsn) {
497662306a36Sopenharmony_ci		free_rsttbl_idx(trtbl, PtrOffset(trtbl, tr));
497762306a36Sopenharmony_ci		goto transaction_table_next;
497862306a36Sopenharmony_ci	}
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci	log->transaction_id = PtrOffset(trtbl, tr);
498162306a36Sopenharmony_ci	undo_next_lsn = le64_to_cpu(tr->undo_next_lsn);
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci	/*
498462306a36Sopenharmony_ci	 * We only have to do anything if the transaction has
498562306a36Sopenharmony_ci	 * something its undo_next_lsn field.
498662306a36Sopenharmony_ci	 */
498762306a36Sopenharmony_ci	if (!undo_next_lsn)
498862306a36Sopenharmony_ci		goto commit_undo;
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci	/* Read the first record to be undone by this transaction. */
499162306a36Sopenharmony_ci	err = read_log_rec_lcb(log, undo_next_lsn, lcb_ctx_undo_next, &lcb);
499262306a36Sopenharmony_ci	if (err)
499362306a36Sopenharmony_ci		goto out;
499462306a36Sopenharmony_ci
499562306a36Sopenharmony_ci	/*
499662306a36Sopenharmony_ci	 * Now loop to read all of our log records forwards,
499762306a36Sopenharmony_ci	 * until we hit the end of the file, cleaning up at the end.
499862306a36Sopenharmony_ci	 */
499962306a36Sopenharmony_ciundo_action_next:
500062306a36Sopenharmony_ci
500162306a36Sopenharmony_ci	lrh = lcb->log_rec;
500262306a36Sopenharmony_ci	frh = lcb->lrh;
500362306a36Sopenharmony_ci	transact_id = le32_to_cpu(frh->transact_id);
500462306a36Sopenharmony_ci	rec_len = le32_to_cpu(frh->client_data_len);
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_ci	if (!check_log_rec(lrh, rec_len, transact_id, bytes_per_attr_entry)) {
500762306a36Sopenharmony_ci		err = -EINVAL;
500862306a36Sopenharmony_ci		goto out;
500962306a36Sopenharmony_ci	}
501062306a36Sopenharmony_ci
501162306a36Sopenharmony_ci	if (lrh->undo_op == cpu_to_le16(Noop))
501262306a36Sopenharmony_ci		goto read_next_log_undo_action;
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci	oe = Add2Ptr(oatbl, le16_to_cpu(lrh->target_attr));
501562306a36Sopenharmony_ci	oa = oe->ptr;
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->lcns_follow);
501862306a36Sopenharmony_ci	if (!t16)
501962306a36Sopenharmony_ci		goto add_allocated_vcns;
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_ci	is_mapped = run_lookup_entry(oa->run1, le64_to_cpu(lrh->target_vcn),
502262306a36Sopenharmony_ci				     &lcn, &clen, NULL);
502362306a36Sopenharmony_ci
502462306a36Sopenharmony_ci	/*
502562306a36Sopenharmony_ci	 * If the mapping isn't already the table or the  mapping
502662306a36Sopenharmony_ci	 * corresponds to a hole the mapping, we need to make sure
502762306a36Sopenharmony_ci	 * there is no partial page already memory.
502862306a36Sopenharmony_ci	 */
502962306a36Sopenharmony_ci	if (is_mapped && lcn != SPARSE_LCN && clen >= t16)
503062306a36Sopenharmony_ci		goto add_allocated_vcns;
503162306a36Sopenharmony_ci
503262306a36Sopenharmony_ci	vcn = le64_to_cpu(lrh->target_vcn);
503362306a36Sopenharmony_ci	vcn &= ~(u64)(log->clst_per_page - 1);
503462306a36Sopenharmony_ci
503562306a36Sopenharmony_ciadd_allocated_vcns:
503662306a36Sopenharmony_ci	for (i = 0, vcn = le64_to_cpu(lrh->target_vcn),
503762306a36Sopenharmony_ci	    size = (vcn + 1) << sbi->cluster_bits;
503862306a36Sopenharmony_ci	     i < t16; i++, vcn += 1, size += sbi->cluster_size) {
503962306a36Sopenharmony_ci		attr = oa->attr;
504062306a36Sopenharmony_ci		if (!attr->non_res) {
504162306a36Sopenharmony_ci			if (size > le32_to_cpu(attr->res.data_size))
504262306a36Sopenharmony_ci				attr->res.data_size = cpu_to_le32(size);
504362306a36Sopenharmony_ci		} else {
504462306a36Sopenharmony_ci			if (size > le64_to_cpu(attr->nres.data_size))
504562306a36Sopenharmony_ci				attr->nres.valid_size = attr->nres.data_size =
504662306a36Sopenharmony_ci					attr->nres.alloc_size =
504762306a36Sopenharmony_ci						cpu_to_le64(size);
504862306a36Sopenharmony_ci		}
504962306a36Sopenharmony_ci	}
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_ci	t16 = le16_to_cpu(lrh->undo_op);
505262306a36Sopenharmony_ci	if (can_skip_action(t16))
505362306a36Sopenharmony_ci		goto read_next_log_undo_action;
505462306a36Sopenharmony_ci
505562306a36Sopenharmony_ci	/* Point to the Redo data and get its length. */
505662306a36Sopenharmony_ci	data = Add2Ptr(lrh, le16_to_cpu(lrh->undo_off));
505762306a36Sopenharmony_ci	dlen = le16_to_cpu(lrh->undo_len);
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_ci	/* It is time to apply the undo action. */
506062306a36Sopenharmony_ci	err = do_action(log, oe, lrh, t16, data, dlen, rec_len, NULL);
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_ciread_next_log_undo_action:
506362306a36Sopenharmony_ci	/*
506462306a36Sopenharmony_ci	 * Keep reading and looping back until we have read the
506562306a36Sopenharmony_ci	 * last record for this transaction.
506662306a36Sopenharmony_ci	 */
506762306a36Sopenharmony_ci	err = read_next_log_rec(log, lcb, &rec_lsn);
506862306a36Sopenharmony_ci	if (err)
506962306a36Sopenharmony_ci		goto out;
507062306a36Sopenharmony_ci
507162306a36Sopenharmony_ci	if (rec_lsn)
507262306a36Sopenharmony_ci		goto undo_action_next;
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ci	lcb_put(lcb);
507562306a36Sopenharmony_ci	lcb = NULL;
507662306a36Sopenharmony_ci
507762306a36Sopenharmony_cicommit_undo:
507862306a36Sopenharmony_ci	free_rsttbl_idx(trtbl, log->transaction_id);
507962306a36Sopenharmony_ci
508062306a36Sopenharmony_ci	log->transaction_id = 0;
508162306a36Sopenharmony_ci
508262306a36Sopenharmony_ci	goto transaction_table_next;
508362306a36Sopenharmony_ci
508462306a36Sopenharmony_ciundo_action_done:
508562306a36Sopenharmony_ci
508662306a36Sopenharmony_ci	ntfs_update_mftmirr(sbi, 0);
508762306a36Sopenharmony_ci
508862306a36Sopenharmony_ci	sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY;
508962306a36Sopenharmony_ci
509062306a36Sopenharmony_ciend_reply:
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_ci	err = 0;
509362306a36Sopenharmony_ci	if (is_ro)
509462306a36Sopenharmony_ci		goto out;
509562306a36Sopenharmony_ci
509662306a36Sopenharmony_ci	rh = kzalloc(log->page_size, GFP_NOFS);
509762306a36Sopenharmony_ci	if (!rh) {
509862306a36Sopenharmony_ci		err = -ENOMEM;
509962306a36Sopenharmony_ci		goto out;
510062306a36Sopenharmony_ci	}
510162306a36Sopenharmony_ci
510262306a36Sopenharmony_ci	rh->rhdr.sign = NTFS_RSTR_SIGNATURE;
510362306a36Sopenharmony_ci	rh->rhdr.fix_off = cpu_to_le16(offsetof(struct RESTART_HDR, fixups));
510462306a36Sopenharmony_ci	t16 = (log->page_size >> SECTOR_SHIFT) + 1;
510562306a36Sopenharmony_ci	rh->rhdr.fix_num = cpu_to_le16(t16);
510662306a36Sopenharmony_ci	rh->sys_page_size = cpu_to_le32(log->page_size);
510762306a36Sopenharmony_ci	rh->page_size = cpu_to_le32(log->page_size);
510862306a36Sopenharmony_ci
510962306a36Sopenharmony_ci	t16 = ALIGN(offsetof(struct RESTART_HDR, fixups) + sizeof(short) * t16,
511062306a36Sopenharmony_ci		    8);
511162306a36Sopenharmony_ci	rh->ra_off = cpu_to_le16(t16);
511262306a36Sopenharmony_ci	rh->minor_ver = cpu_to_le16(1); // 0x1A:
511362306a36Sopenharmony_ci	rh->major_ver = cpu_to_le16(1); // 0x1C:
511462306a36Sopenharmony_ci
511562306a36Sopenharmony_ci	ra2 = Add2Ptr(rh, t16);
511662306a36Sopenharmony_ci	memcpy(ra2, ra, sizeof(struct RESTART_AREA));
511762306a36Sopenharmony_ci
511862306a36Sopenharmony_ci	ra2->client_idx[0] = 0;
511962306a36Sopenharmony_ci	ra2->client_idx[1] = LFS_NO_CLIENT_LE;
512062306a36Sopenharmony_ci	ra2->flags = cpu_to_le16(2);
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci	le32_add_cpu(&ra2->open_log_count, 1);
512362306a36Sopenharmony_ci
512462306a36Sopenharmony_ci	ntfs_fix_pre_write(&rh->rhdr, log->page_size);
512562306a36Sopenharmony_ci
512662306a36Sopenharmony_ci	err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rh, log->page_size, 0);
512762306a36Sopenharmony_ci	if (!err)
512862306a36Sopenharmony_ci		err = ntfs_sb_write_run(sbi, &log->ni->file.run, log->page_size,
512962306a36Sopenharmony_ci					rh, log->page_size, 0);
513062306a36Sopenharmony_ci
513162306a36Sopenharmony_ci	kfree(rh);
513262306a36Sopenharmony_ci	if (err)
513362306a36Sopenharmony_ci		goto out;
513462306a36Sopenharmony_ci
513562306a36Sopenharmony_ciout:
513662306a36Sopenharmony_ci	kfree(rst);
513762306a36Sopenharmony_ci	if (lcb)
513862306a36Sopenharmony_ci		lcb_put(lcb);
513962306a36Sopenharmony_ci
514062306a36Sopenharmony_ci	/*
514162306a36Sopenharmony_ci	 * Scan the Open Attribute Table to close all of
514262306a36Sopenharmony_ci	 * the open attributes.
514362306a36Sopenharmony_ci	 */
514462306a36Sopenharmony_ci	oe = NULL;
514562306a36Sopenharmony_ci	while ((oe = enum_rstbl(oatbl, oe))) {
514662306a36Sopenharmony_ci		rno = ino_get(&oe->ref);
514762306a36Sopenharmony_ci
514862306a36Sopenharmony_ci		if (oe->is_attr_name == 1) {
514962306a36Sopenharmony_ci			kfree(oe->ptr);
515062306a36Sopenharmony_ci			oe->ptr = NULL;
515162306a36Sopenharmony_ci			continue;
515262306a36Sopenharmony_ci		}
515362306a36Sopenharmony_ci
515462306a36Sopenharmony_ci		if (oe->is_attr_name)
515562306a36Sopenharmony_ci			continue;
515662306a36Sopenharmony_ci
515762306a36Sopenharmony_ci		oa = oe->ptr;
515862306a36Sopenharmony_ci		if (!oa)
515962306a36Sopenharmony_ci			continue;
516062306a36Sopenharmony_ci
516162306a36Sopenharmony_ci		run_close(&oa->run0);
516262306a36Sopenharmony_ci		kfree(oa->attr);
516362306a36Sopenharmony_ci		if (oa->ni)
516462306a36Sopenharmony_ci			iput(&oa->ni->vfs_inode);
516562306a36Sopenharmony_ci		kfree(oa);
516662306a36Sopenharmony_ci	}
516762306a36Sopenharmony_ci
516862306a36Sopenharmony_ci	kfree(trtbl);
516962306a36Sopenharmony_ci	kfree(oatbl);
517062306a36Sopenharmony_ci	kfree(dptbl);
517162306a36Sopenharmony_ci	kfree(attr_names);
517262306a36Sopenharmony_ci	kfree(log->rst_info.r_page);
517362306a36Sopenharmony_ci
517462306a36Sopenharmony_ci	kfree(ra);
517562306a36Sopenharmony_ci	kfree(log->one_page_buf);
517662306a36Sopenharmony_ci
517762306a36Sopenharmony_ci	if (err)
517862306a36Sopenharmony_ci		sbi->flags |= NTFS_FLAGS_NEED_REPLAY;
517962306a36Sopenharmony_ci
518062306a36Sopenharmony_ci	if (err == -EROFS)
518162306a36Sopenharmony_ci		err = 0;
518262306a36Sopenharmony_ci	else if (log->set_dirty)
518362306a36Sopenharmony_ci		ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
518462306a36Sopenharmony_ci
518562306a36Sopenharmony_ci	kfree(log);
518662306a36Sopenharmony_ci
518762306a36Sopenharmony_ci	return err;
518862306a36Sopenharmony_ci}
5189