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, ¤t_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